Skip to content

Commit 91173c4

Browse files
dbieberclaude
andcommitted
Make profile names and shortcuts user-configurable
Instead of hardcoding profile names (roam, work, assistant, guest), profiles now store their name and shortcut as metadata. This makes the feature work for any GNG user, not just David-specific configs. Changes: - Profiles now store: {name, shortcut, settings} - Shortcuts mapped dynamically in Redis (GoNoteGo:profile_shortcuts) - New command: `profile save <name> <number>` to set shortcuts - `profile list` shows shortcuts: "roam (:1)" - Numeric shortcuts (`:1`, `:2`) look up mapping dynamically - Backward compatible with old profile format Example usage: :profile save roam 1 # Save as "roam" with shortcut :1 :1 # Load roam profile :profile list # Shows: roam (:1), work (:2), backup 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent c3c0e8c commit 91173c4

File tree

3 files changed

+146
-72
lines changed

3 files changed

+146
-72
lines changed

PROFILES.md

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,27 @@
11
# Go Note Go Profiles
22

3-
Profiles allow you to quickly switch between different Go Note Go configurations. Each profile stores a complete snapshot of all settings.
3+
Profiles allow you to quickly switch between different Go Note Go configurations. Each profile stores a complete snapshot of all settings, plus a name and optional numeric shortcut.
44

55
## Quick Start
66

7-
### Initialize Default Profiles
7+
### Create Your First Profile
8+
9+
Set up your settings the way you want them, then save as a profile with a shortcut:
810

9-
First, set up your settings the way you want them, then run:
1011
```
11-
:profile init
12+
:profile save roam 1
1213
```
1314

14-
This creates four default profiles:
15-
- `roam` (shortcut: `:1`)
16-
- `work` (shortcut: `:2`)
17-
- `assistant` (shortcut: `:3`)
18-
- `guest` (shortcut: `:4`)
15+
This creates a profile named "roam" with shortcut `:1`.
1916

2017
### Switch Between Profiles
2118

22-
Use the numeric shortcuts:
19+
Use the numeric shortcuts you've configured:
2320
```
24-
:1 # Switch to roam profile
25-
:2 # Switch to work profile
26-
:3 # Switch to assistant profile
27-
:4 # Switch to guest profile
21+
:1 # Switch to profile with shortcut 1
22+
:2 # Switch to profile with shortcut 2
23+
:3 # Switch to profile with shortcut 3
24+
:4 # Switch to profile with shortcut 4
2825
```
2926

3027
Or use the full command:
@@ -36,21 +33,32 @@ Or use the full command:
3633

3734
### Save a Profile
3835
```
39-
:profile save <name>
36+
:profile save <name> # Save without shortcut
37+
:profile save <name> <number> # Save with numeric shortcut
38+
```
39+
40+
Examples:
41+
```
42+
:profile save roam 1 # Save as "roam" with shortcut :1
43+
:profile save work 2 # Save as "work" with shortcut :2
44+
:profile save temp # Save as "temp" with no shortcut
4045
```
41-
Saves your current settings as a named profile.
4246

4347
### Load a Profile
4448
```
45-
:profile load <name>
49+
:profile load <name> # Load by name
50+
:<number> # Load by shortcut (if configured)
4651
```
47-
Loads all settings from the specified profile. Your current settings are automatically backed up to the `backup` profile before loading.
52+
53+
Your current settings are automatically backed up to the `backup` profile before loading.
4854

4955
### List Profiles
5056
```
5157
:profile list
5258
```
53-
Shows all saved profiles.
59+
Shows all saved profiles with their shortcuts (if any).
60+
61+
Example output: `Profiles: backup, roam (:1), temp, work (:2)`
5462

5563
### Current Profile
5664
```
@@ -62,30 +70,46 @@ Shows which profile is currently active.
6270
```
6371
:profile delete <name>
6472
```
65-
Deletes a saved profile.
73+
Deletes a saved profile and removes its shortcut mapping.
6674

6775
## Example Use Cases
6876

6977
### Personal vs Work
70-
- Profile 1 (roam): Personal Roam graph with your personal account
71-
- Profile 2 (work): Work Roam graph or different note-taking system
78+
```
79+
:profile save personal 1 # Personal Roam graph
80+
:profile save work 2 # Work note-taking system
81+
```
82+
83+
Then switch with `:1` or `:2`
7284

7385
### Different Assistants
74-
- Profile 3 (assistant): Connected to your personal assistant
75-
- Profile 4 (guest): Safe settings for letting others try your GNG
86+
```
87+
:profile save assistant 3 # Connected to personal assistant
88+
:profile save guest 4 # Safe settings for demos
89+
```
7690

7791
### Different Upload Destinations
78-
Switch between different note-taking systems (Roam, RemNote, Notion, etc.) with a single command.
92+
Switch between Roam, RemNote, Notion, etc. with a single command.
7993

8094
## How It Works
8195

82-
- Each profile is stored as a JSON blob in Redis containing all setting values
83-
- When you load a profile, your current settings are automatically backed up
84-
- The `backup` profile always contains your most recent settings before the last profile switch
85-
- Settings include: uploader type, credentials, custom command paths, API keys, etc.
96+
- Each profile stores all settings from `secure_settings.py` as JSON in Redis
97+
- Profile metadata (name, shortcut) is stored with the settings
98+
- Shortcuts are mapped dynamically in Redis (no hardcoded values)
99+
- When you load a profile, current settings are backed up to `backup`
100+
- The `backup` profile always contains your most recent settings
86101

87102
## Safety
88103

89104
- Your current settings are always backed up to `backup` before loading a new profile
90105
- The secure_settings.py file is never modified - profiles only affect Redis settings
91-
- You can always revert to your previous settings by running `:profile load backup`
106+
- You can always revert: `:profile load backup`
107+
- Deleting a profile also removes its shortcut mapping
108+
109+
## Configuration
110+
111+
Profiles are completely user-configurable:
112+
- Choose your own profile names
113+
- Assign any numeric shortcuts you want
114+
- No hardcoded profile names in the codebase
115+
- Works for any Go Note Go user

gonotego/command_center/profile_commands.py

Lines changed: 16 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,32 +8,32 @@
88
say = system_commands.say
99

1010

11-
# Predefined profile shortcuts
12-
PROFILE_SHORTCUTS = {
13-
'1': 'roam',
14-
'2': 'work',
15-
'3': 'assistant',
16-
'4': 'guest',
17-
}
18-
19-
2011
@register_command('{}')
2112
def load_profile_shortcut(shortcut):
22-
"""Load a profile using numeric shortcut (e.g., :1 for roam)."""
23-
if shortcut not in PROFILE_SHORTCUTS:
13+
"""Load a profile using numeric shortcut (e.g., :1, :2, :3)."""
14+
# Check if this is a numeric shortcut
15+
if not shortcut.isdigit():
2416
return # Not a profile shortcut, let other commands handle it
2517

26-
profile_name = PROFILE_SHORTCUTS[shortcut]
18+
profile_name = profiles.get_profile_by_shortcut(shortcut)
19+
if profile_name is None:
20+
return # No profile mapped to this shortcut
21+
2722
result = profiles.load_profile(profile_name)
2823

2924
if result is None:
30-
say(f'Profile {profile_name} not found. Creating from current settings.')
31-
profiles.save_profile(profile_name)
32-
say(f'Saved current settings as {profile_name}.')
25+
say(f'Profile {profile_name} not found.')
3326
else:
3427
say(f'Loaded profile: {profile_name}')
3528

3629

30+
@register_command('profile save {} {}')
31+
def save_profile_with_shortcut(profile_name, shortcut):
32+
"""Save current settings as a named profile with a shortcut."""
33+
profiles.save_profile(profile_name, shortcut=shortcut)
34+
say(f'Saved profile: {profile_name} with shortcut :{shortcut}')
35+
36+
3737
@register_command('profile save {}')
3838
def save_profile(profile_name):
3939
"""Save current settings as a named profile."""
@@ -53,7 +53,7 @@ def load_profile(profile_name):
5353

5454
@register_command('profile list')
5555
def list_profiles():
56-
"""List all saved profiles."""
56+
"""List all saved profiles with their shortcuts."""
5757
profile_names = profiles.list_profiles()
5858
if not profile_names:
5959
say('No profiles saved.')
@@ -76,14 +76,3 @@ def delete_profile(profile_name):
7676
"""Delete a saved profile."""
7777
profiles.delete_profile(profile_name)
7878
say(f'Deleted profile: {profile_name}')
79-
80-
81-
@register_command('profile init')
82-
def init_default_profiles():
83-
"""Initialize default profiles (roam, work, assistant, guest) from current settings."""
84-
current_settings = profiles.get_all_settings()
85-
86-
for shortcut, profile_name in PROFILE_SHORTCUTS.items():
87-
profiles.save_profile(profile_name)
88-
89-
say('Initialized default profiles: roam, work, assistant, guest')

gonotego/settings/profiles.py

Lines changed: 76 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
"""Profile management for Go Note Go settings.
22
3-
Profiles allow switching between different configurations (roam, work, assistant, guest).
4-
Each profile stores a complete snapshot of all settings.
3+
Profiles allow switching between different configurations.
4+
Each profile stores a complete snapshot of all settings plus metadata (name, shortcut).
55
"""
66
import json
77
from gonotego.settings import settings
88
from gonotego.settings import secure_settings
99
from gonotego.common import interprocess
1010

1111
PROFILES_KEY = 'GoNoteGo:profiles'
12+
PROFILE_SHORTCUTS_KEY = 'GoNoteGo:profile_shortcuts'
1213
CURRENT_PROFILE_KEY = 'GoNoteGo:current_profile'
13-
BACKUP_PROFILE_KEY = 'GoNoteGo:backup_profile'
1414

1515

1616
def get_redis_key(profile_name):
@@ -28,12 +28,33 @@ def get_all_settings():
2828
return settings_dict
2929

3030

31-
def save_profile(profile_name):
32-
"""Save current settings to a named profile."""
31+
def save_profile(profile_name, shortcut=None):
32+
"""Save current settings to a named profile.
33+
34+
Args:
35+
profile_name: Name of the profile
36+
shortcut: Optional numeric shortcut (e.g., '1', '2', '3')
37+
"""
3338
r = interprocess.get_redis_client()
3439
current_settings = get_all_settings()
35-
settings_json = json.dumps(current_settings)
36-
r.set(get_redis_key(profile_name), settings_json)
40+
41+
# Store profile data
42+
profile_data = {
43+
'name': profile_name,
44+
'settings': current_settings,
45+
}
46+
if shortcut is not None:
47+
profile_data['shortcut'] = shortcut
48+
49+
profile_json = json.dumps(profile_data)
50+
r.set(get_redis_key(profile_name), profile_json)
51+
52+
# Update shortcuts mapping if shortcut is provided
53+
if shortcut is not None:
54+
shortcuts = get_shortcuts_mapping()
55+
shortcuts[shortcut] = profile_name
56+
r.set(PROFILE_SHORTCUTS_KEY, json.dumps(shortcuts))
57+
3758
return current_settings
3859

3960

@@ -46,15 +67,16 @@ def load_profile(profile_name):
4667
"""
4768
r = interprocess.get_redis_client()
4869

49-
# First, backup current settings
50-
save_profile('backup')
70+
# First, backup current settings (without shortcut)
71+
save_profile('backup', shortcut=None)
5172

5273
# Load the requested profile
5374
profile_json = r.get(get_redis_key(profile_name))
5475
if profile_json is None:
5576
return None
5677

57-
profile_settings = json.loads(profile_json)
78+
profile_data = json.loads(profile_json)
79+
profile_settings = profile_data.get('settings', profile_data) # Backward compat
5880

5981
# Clear all current settings and load profile settings
6082
settings.clear_all()
@@ -76,21 +98,60 @@ def get_current_profile():
7698
return profile_bytes.decode('utf-8')
7799

78100

101+
def get_shortcuts_mapping():
102+
"""Get the mapping of shortcuts to profile names."""
103+
r = interprocess.get_redis_client()
104+
shortcuts_json = r.get(PROFILE_SHORTCUTS_KEY)
105+
if shortcuts_json is None:
106+
return {}
107+
return json.loads(shortcuts_json)
108+
109+
110+
def get_profile_by_shortcut(shortcut):
111+
"""Get profile name for a given shortcut."""
112+
shortcuts = get_shortcuts_mapping()
113+
return shortcuts.get(shortcut)
114+
115+
79116
def list_profiles():
80-
"""List all saved profile names."""
117+
"""List all saved profile names with their shortcuts."""
81118
r = interprocess.get_redis_client()
82119
profile_keys = r.keys(get_redis_key('*'))
83-
profile_names = []
120+
121+
profiles_info = []
122+
shortcuts = get_shortcuts_mapping()
123+
# Reverse mapping for lookup
124+
name_to_shortcut = {v: k for k, v in shortcuts.items()}
125+
84126
for key in profile_keys:
85127
key_str = key.decode('utf-8') if isinstance(key, bytes) else key
86128
# Extract profile name from key
87129
if key_str.startswith(f'{PROFILES_KEY}:'):
88130
profile_name = key_str[len(f'{PROFILES_KEY}:'):]
89-
profile_names.append(profile_name)
90-
return sorted(profile_names)
131+
shortcut = name_to_shortcut.get(profile_name)
132+
if shortcut:
133+
profiles_info.append(f"{profile_name} (:{shortcut})")
134+
else:
135+
profiles_info.append(profile_name)
136+
137+
return sorted(profiles_info)
91138

92139

93140
def delete_profile(profile_name):
94-
"""Delete a saved profile."""
141+
"""Delete a saved profile and remove its shortcut if any."""
95142
r = interprocess.get_redis_client()
143+
144+
# Remove from shortcuts mapping
145+
shortcuts = get_shortcuts_mapping()
146+
shortcut_to_remove = None
147+
for shortcut, name in shortcuts.items():
148+
if name == profile_name:
149+
shortcut_to_remove = shortcut
150+
break
151+
152+
if shortcut_to_remove:
153+
del shortcuts[shortcut_to_remove]
154+
r.set(PROFILE_SHORTCUTS_KEY, json.dumps(shortcuts))
155+
156+
# Delete the profile
96157
r.delete(get_redis_key(profile_name))

0 commit comments

Comments
 (0)