@admin-layout/tailwind-ui 12.2.4-alpha.63 → 12.2.4-alpha.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/AIToolbar/components/completion/Files.d.ts +3 -3
- package/lib/components/AIToolbar/components/completion/Files.d.ts.map +1 -1
- package/lib/components/AIToolbar/components/completion/Files.js +66 -36
- package/lib/components/AIToolbar/components/completion/Files.js.map +1 -1
- package/lib/components/AIToolbar/components/completion/Input.js +1 -1
- package/lib/components/AIToolbar/components/speech/Warning.d.ts.map +1 -1
- package/lib/components/AIToolbar/components/speech/Warning.js +101 -29
- package/lib/components/AIToolbar/components/speech/Warning.js.map +1 -1
- package/lib/components/AIToolbar/hooks/useActiveCharacter.d.ts +2 -0
- package/lib/components/AIToolbar/hooks/useActiveCharacter.d.ts.map +1 -1
- package/lib/components/AIToolbar/hooks/useActiveCharacter.js +1 -2
- package/lib/components/AIToolbar/hooks/useActiveCharacter.js.map +1 -1
- package/lib/components/CharacterSlotPicker/CharacterSlotPicker.d.ts +20 -0
- package/lib/components/CharacterSlotPicker/CharacterSlotPicker.d.ts.map +1 -0
- package/lib/components/CharacterSlotPicker/CharacterSlotPicker.js +249 -0
- package/lib/components/CharacterSlotPicker/CharacterSlotPicker.js.map +1 -0
- package/lib/components/CharacterSlotPicker/index.d.ts +3 -0
- package/lib/components/CharacterSlotPicker/index.d.ts.map +1 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.d.ts.map +1 -1
- package/lib/hooks/activeCharacterPreference.d.ts +2 -1
- package/lib/hooks/activeCharacterPreference.d.ts.map +1 -1
- package/lib/hooks/activeCharacterPreference.js +9 -2
- package/lib/hooks/activeCharacterPreference.js.map +1 -1
- package/lib/hooks/characterOverrides.d.ts +29 -0
- package/lib/hooks/characterOverrides.d.ts.map +1 -0
- package/lib/hooks/characterOverrides.js +130 -0
- package/lib/hooks/characterOverrides.js.map +1 -0
- package/lib/hooks/characterSlotsPreference.d.ts +33 -0
- package/lib/hooks/characterSlotsPreference.d.ts.map +1 -0
- package/lib/hooks/characterSlotsPreference.js +141 -0
- package/lib/hooks/characterSlotsPreference.js.map +1 -0
- package/lib/hooks/index.d.ts +3 -0
- package/lib/hooks/index.d.ts.map +1 -1
- package/lib/hooks/useGlobalShortcutBridge.d.ts +21 -0
- package/lib/hooks/useGlobalShortcutBridge.d.ts.map +1 -0
- package/lib/hooks/useGlobalShortcutBridge.js +68 -0
- package/lib/hooks/useGlobalShortcutBridge.js.map +1 -0
- package/lib/index.js +1 -1
- package/lib/shardui/command.d.ts +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import {jsx,jsxs}from'react/jsx-runtime';import*as React from'react';import {Star,X,Settings}from'lucide-react';import {Popover,PopoverTrigger,PopoverContent}from'../../shardui/popover.js';import {useCharacterPicker}from'../AIToolbar/hooks/useActiveCharacter.js';import {useCharacterSlots,SLOT_NUMBERS}from'../../hooks/characterSlotsPreference.js';import {useCharacterOverrides,setCharacterOverrides}from'../../hooks/characterOverrides.js';// helpers
|
|
2
|
+
function findCharacterByFile(list, fileName) {
|
|
3
|
+
if (!fileName) return null;
|
|
4
|
+
return list.find(c => c.fileName === fileName) ?? null;
|
|
5
|
+
}
|
|
6
|
+
function SlotSettingsForm({
|
|
7
|
+
fileName,
|
|
8
|
+
characterName,
|
|
9
|
+
onClose
|
|
10
|
+
}) {
|
|
11
|
+
const overrides = useCharacterOverrides(fileName);
|
|
12
|
+
const [voiceId, setVoiceId] = React.useState(overrides.voiceId);
|
|
13
|
+
const [addendum, setAddendum] = React.useState(overrides.addendum);
|
|
14
|
+
// Keep local state in sync if the popover re-opens for a different snapshot.
|
|
15
|
+
React.useEffect(() => {
|
|
16
|
+
setVoiceId(overrides.voiceId);
|
|
17
|
+
setAddendum(overrides.addendum);
|
|
18
|
+
}, [overrides.voiceId, overrides.addendum]);
|
|
19
|
+
const handleSave = () => {
|
|
20
|
+
setCharacterOverrides(fileName, {
|
|
21
|
+
voiceId: voiceId.trim(),
|
|
22
|
+
addendum: addendum.trim()
|
|
23
|
+
});
|
|
24
|
+
onClose();
|
|
25
|
+
};
|
|
26
|
+
return jsxs("div", {
|
|
27
|
+
className: "flex flex-col gap-2 p-1",
|
|
28
|
+
children: [jsx("div", {
|
|
29
|
+
className: "px-1 text-xs font-semibold text-foreground/80",
|
|
30
|
+
children: characterName
|
|
31
|
+
}), jsxs("label", {
|
|
32
|
+
className: "flex flex-col gap-1",
|
|
33
|
+
children: [jsx("span", {
|
|
34
|
+
className: "text-xs text-muted-foreground",
|
|
35
|
+
children: "TTS voice ID"
|
|
36
|
+
}), jsx("input", {
|
|
37
|
+
type: "text",
|
|
38
|
+
value: voiceId,
|
|
39
|
+
onChange: e => setVoiceId(e.target.value),
|
|
40
|
+
placeholder: "e.g. ElevenLabs voice id",
|
|
41
|
+
spellCheck: false,
|
|
42
|
+
className: "w-full rounded-md border border-border bg-background px-2 py-1 text-xs font-mono outline-none focus:border-primary"
|
|
43
|
+
})]
|
|
44
|
+
}), jsxs("label", {
|
|
45
|
+
className: "flex flex-col gap-1",
|
|
46
|
+
children: [jsx("span", {
|
|
47
|
+
className: "text-xs text-muted-foreground",
|
|
48
|
+
children: "Prompt addendum"
|
|
49
|
+
}), jsx("textarea", {
|
|
50
|
+
value: addendum,
|
|
51
|
+
onChange: e => setAddendum(e.target.value),
|
|
52
|
+
rows: 4,
|
|
53
|
+
placeholder: "Extra instructions appended to this character's persona\u2026",
|
|
54
|
+
className: "w-full resize-y rounded-md border border-border bg-background px-2 py-1 text-xs outline-none focus:border-primary"
|
|
55
|
+
})]
|
|
56
|
+
}), jsxs("div", {
|
|
57
|
+
className: "flex justify-end gap-2 pt-1",
|
|
58
|
+
children: [jsx("button", {
|
|
59
|
+
type: "button",
|
|
60
|
+
onClick: onClose,
|
|
61
|
+
className: "rounded-md px-2 py-1 text-xs text-muted-foreground hover:bg-muted",
|
|
62
|
+
children: "Cancel"
|
|
63
|
+
}), jsx("button", {
|
|
64
|
+
type: "button",
|
|
65
|
+
onClick: handleSave,
|
|
66
|
+
className: "rounded-md bg-primary px-2 py-1 text-xs font-medium text-primary-foreground hover:opacity-90",
|
|
67
|
+
children: "Save"
|
|
68
|
+
})]
|
|
69
|
+
})]
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
function SlotTile({
|
|
73
|
+
slot,
|
|
74
|
+
fileName,
|
|
75
|
+
character,
|
|
76
|
+
isDefault,
|
|
77
|
+
onPick,
|
|
78
|
+
onClear,
|
|
79
|
+
onActivate,
|
|
80
|
+
onSetDefault,
|
|
81
|
+
candidates
|
|
82
|
+
}) {
|
|
83
|
+
const [open, setOpen] = React.useState(false);
|
|
84
|
+
const [settingsOpen, setSettingsOpen] = React.useState(false);
|
|
85
|
+
const filled = Boolean(fileName);
|
|
86
|
+
const handleTileClick = () => {
|
|
87
|
+
if (filled) onActivate(slot);else setOpen(true);
|
|
88
|
+
};
|
|
89
|
+
const handleContextMenu = e => {
|
|
90
|
+
if (!filled) return;
|
|
91
|
+
e.preventDefault();
|
|
92
|
+
onClear(slot);
|
|
93
|
+
};
|
|
94
|
+
return jsxs(Popover, {
|
|
95
|
+
open: open && !filled,
|
|
96
|
+
onOpenChange: setOpen,
|
|
97
|
+
children: [jsx(PopoverTrigger, {
|
|
98
|
+
asChild: true,
|
|
99
|
+
children: jsxs("button", {
|
|
100
|
+
type: "button",
|
|
101
|
+
onClick: handleTileClick,
|
|
102
|
+
onContextMenu: handleContextMenu,
|
|
103
|
+
title: filled ? `Slot ${slot} · ${character?.name ?? fileName} (click to activate, right-click to clear)` : `Slot ${slot} · empty (click to assign)`,
|
|
104
|
+
className: ['group relative aspect-square w-full overflow-hidden rounded-xl border bg-muted/30 transition', 'flex items-center justify-center', filled ? 'border-border hover:border-primary/60' : 'border-dashed border-border/60 hover:border-primary/50 hover:bg-muted/50', isDefault ? 'ring-2 ring-primary ring-offset-2 ring-offset-background' : ''].join(' '),
|
|
105
|
+
children: [jsx("span", {
|
|
106
|
+
className: "absolute left-1 top-1 z-10 rounded bg-background/80 px-1 text-xs font-semibold text-foreground/70",
|
|
107
|
+
children: slot
|
|
108
|
+
}), filled && character?.avatarSrc ? jsx("img", {
|
|
109
|
+
src: character.avatarSrc,
|
|
110
|
+
alt: character.name,
|
|
111
|
+
className: "h-full w-full object-cover"
|
|
112
|
+
}) : filled ? jsx("span", {
|
|
113
|
+
className: "px-2 text-center text-sm font-medium text-foreground/80",
|
|
114
|
+
children: character?.name ?? fileName
|
|
115
|
+
}) : jsx("span", {
|
|
116
|
+
className: "text-2xl text-muted-foreground/60",
|
|
117
|
+
children: "+"
|
|
118
|
+
}), filled && jsx("button", {
|
|
119
|
+
type: "button",
|
|
120
|
+
onClick: e => {
|
|
121
|
+
e.stopPropagation();
|
|
122
|
+
onSetDefault(slot);
|
|
123
|
+
},
|
|
124
|
+
title: isDefault ? 'Default slot' : 'Set as default',
|
|
125
|
+
className: ['absolute right-1 top-1 z-10 rounded-full p-0.5 transition', isDefault ? 'bg-primary text-primary-foreground' : 'bg-background/80 text-muted-foreground opacity-0 group-hover:opacity-100 hover:text-foreground'].join(' '),
|
|
126
|
+
children: jsx(Star, {
|
|
127
|
+
className: "h-3 w-3",
|
|
128
|
+
fill: isDefault ? 'currentColor' : 'none'
|
|
129
|
+
})
|
|
130
|
+
}), filled && jsx("button", {
|
|
131
|
+
type: "button",
|
|
132
|
+
onClick: e => {
|
|
133
|
+
e.stopPropagation();
|
|
134
|
+
onClear(slot);
|
|
135
|
+
},
|
|
136
|
+
title: "Clear slot",
|
|
137
|
+
className: "absolute bottom-1 right-1 z-10 rounded-full bg-background/80 p-0.5 text-muted-foreground opacity-0 transition group-hover:opacity-100 hover:text-destructive",
|
|
138
|
+
children: jsx(X, {
|
|
139
|
+
className: "h-3 w-3"
|
|
140
|
+
})
|
|
141
|
+
}), filled && fileName && jsxs(Popover, {
|
|
142
|
+
open: settingsOpen,
|
|
143
|
+
onOpenChange: setSettingsOpen,
|
|
144
|
+
children: [jsx(PopoverTrigger, {
|
|
145
|
+
asChild: true,
|
|
146
|
+
children: jsx("button", {
|
|
147
|
+
type: "button",
|
|
148
|
+
onClick: e => {
|
|
149
|
+
e.stopPropagation();
|
|
150
|
+
setSettingsOpen(true);
|
|
151
|
+
},
|
|
152
|
+
title: "Slot settings (voice id & prompt addendum)",
|
|
153
|
+
className: "absolute bottom-1 left-1 z-10 rounded-full bg-background/80 p-0.5 text-muted-foreground opacity-0 transition group-hover:opacity-100 hover:text-foreground",
|
|
154
|
+
children: jsx(Settings, {
|
|
155
|
+
className: "h-3 w-3"
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
}), jsx(PopoverContent, {
|
|
159
|
+
align: "start",
|
|
160
|
+
sideOffset: 6,
|
|
161
|
+
className: "w-72 p-2",
|
|
162
|
+
onClick: e => e.stopPropagation(),
|
|
163
|
+
children: jsx(SlotSettingsForm, {
|
|
164
|
+
fileName: fileName,
|
|
165
|
+
characterName: character?.name ?? fileName,
|
|
166
|
+
onClose: () => setSettingsOpen(false)
|
|
167
|
+
})
|
|
168
|
+
})]
|
|
169
|
+
})]
|
|
170
|
+
})
|
|
171
|
+
}), jsx(PopoverContent, {
|
|
172
|
+
align: "start",
|
|
173
|
+
sideOffset: 6,
|
|
174
|
+
className: "w-56 p-1",
|
|
175
|
+
children: jsx("div", {
|
|
176
|
+
className: "max-h-64 overflow-y-auto",
|
|
177
|
+
children: candidates.length === 0 ? jsx("p", {
|
|
178
|
+
className: "px-2 py-4 text-center text-sm text-muted-foreground",
|
|
179
|
+
children: "No characters installed."
|
|
180
|
+
}) : candidates.map(c => jsxs("button", {
|
|
181
|
+
type: "button",
|
|
182
|
+
onClick: () => {
|
|
183
|
+
onPick(slot, c.name, c.fileName);
|
|
184
|
+
setOpen(false);
|
|
185
|
+
},
|
|
186
|
+
className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-muted",
|
|
187
|
+
children: [c.avatarSrc ? jsx("img", {
|
|
188
|
+
src: c.avatarSrc,
|
|
189
|
+
alt: "",
|
|
190
|
+
className: "h-6 w-6 rounded object-cover"
|
|
191
|
+
}) : jsx("span", {
|
|
192
|
+
className: "inline-block h-6 w-6 rounded bg-muted"
|
|
193
|
+
}), jsx("span", {
|
|
194
|
+
className: "truncate",
|
|
195
|
+
children: c.name
|
|
196
|
+
})]
|
|
197
|
+
}, c.fileName ?? c.name))
|
|
198
|
+
})
|
|
199
|
+
})]
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
function CharacterSlotPicker({
|
|
203
|
+
className
|
|
204
|
+
}) {
|
|
205
|
+
const picker = useCharacterPicker();
|
|
206
|
+
const {
|
|
207
|
+
slots,
|
|
208
|
+
defaultSlot,
|
|
209
|
+
setSlot,
|
|
210
|
+
setDefaultSlot
|
|
211
|
+
} = useCharacterSlots();
|
|
212
|
+
const handlePick = React.useCallback((slot, name, fileName) => {
|
|
213
|
+
if (!fileName) return;
|
|
214
|
+
void setSlot(slot, fileName);
|
|
215
|
+
}, [setSlot]);
|
|
216
|
+
const handleClear = React.useCallback(slot => {
|
|
217
|
+
void setSlot(slot, null);
|
|
218
|
+
}, [setSlot]);
|
|
219
|
+
const handleActivate = React.useCallback(slot => {
|
|
220
|
+
const fileName = slots[slot];
|
|
221
|
+
const c = findCharacterByFile(picker.characters, fileName);
|
|
222
|
+
if (c?.name) picker.setActive(c.name);
|
|
223
|
+
}, [picker, slots]);
|
|
224
|
+
const handleSetDefault = React.useCallback(slot => {
|
|
225
|
+
void setDefaultSlot(slot);
|
|
226
|
+
// Also activate the default slot's character so the change is visible.
|
|
227
|
+
const fileName = slots[slot];
|
|
228
|
+
const c = findCharacterByFile(picker.characters, fileName);
|
|
229
|
+
if (c?.name) picker.setActive(c.name);
|
|
230
|
+
}, [picker, slots, setDefaultSlot]);
|
|
231
|
+
return jsx("div", {
|
|
232
|
+
className: ['grid grid-cols-3 gap-2', className ?? ''].join(' '),
|
|
233
|
+
children: SLOT_NUMBERS.map(slot => {
|
|
234
|
+
const fileName = slots[slot];
|
|
235
|
+
const character = findCharacterByFile(picker.characters, fileName);
|
|
236
|
+
return jsx(SlotTile, {
|
|
237
|
+
slot: slot,
|
|
238
|
+
fileName: fileName,
|
|
239
|
+
character: character,
|
|
240
|
+
isDefault: slot === defaultSlot,
|
|
241
|
+
onPick: handlePick,
|
|
242
|
+
onClear: handleClear,
|
|
243
|
+
onActivate: handleActivate,
|
|
244
|
+
onSetDefault: handleSetDefault,
|
|
245
|
+
candidates: picker.characters
|
|
246
|
+
}, slot);
|
|
247
|
+
})
|
|
248
|
+
});
|
|
249
|
+
}export{CharacterSlotPicker};//# sourceMappingURL=CharacterSlotPicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CharacterSlotPicker.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/CharacterSlotPicker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -14,6 +14,7 @@ export * from './ThemeProvider';
|
|
|
14
14
|
export * from './LanguageMenuDropdown';
|
|
15
15
|
export * from './InputToolBar';
|
|
16
16
|
export * from './AIToolbar';
|
|
17
|
+
export * from './CharacterSlotPicker';
|
|
17
18
|
export * from './Inbox';
|
|
18
19
|
export * from './YantraIcon';
|
|
19
20
|
export { Markdown as AIToolbarMarkdown } from './AIToolbar/components/Markdown';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,eAAe,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAChF,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,WAAW,MAAM,eAAe,CAAC;AACxC,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,UAAU,CAAC;AACzB,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,QAAQ,IAAI,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAChF,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,CAAC"}
|
|
@@ -17,7 +17,8 @@ export declare function readPreferredActiveCharacterFileName(): string | null;
|
|
|
17
17
|
* active character (toolbar + `/chat`).
|
|
18
18
|
*/
|
|
19
19
|
export declare function setPreferredActiveCharacterFileName(fileName: string | null): Promise<void>;
|
|
20
|
-
/** Pick the character row that matches the stored preference; otherwise
|
|
20
|
+
/** Pick the character row that matches the stored preference; otherwise the
|
|
21
|
+
* default-slot character (see `characterSlotsPreference`); otherwise list[0]. */
|
|
21
22
|
export declare function resolveActiveCharacterFromList<T extends {
|
|
22
23
|
file_name?: string | null;
|
|
23
24
|
}>(list: T[] | null | undefined): T | null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activeCharacterPreference.d.ts","sourceRoot":"","sources":["../../src/hooks/activeCharacterPreference.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"activeCharacterPreference.d.ts","sourceRoot":"","sources":["../../src/hooks/activeCharacterPreference.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,0EAA0E;AAC1E,eAAO,MAAM,sCAAsC,sCAAsC,CAAC;AAE1F,0GAA0G;AAC1G,eAAO,MAAM,6BAA6B,mCAAmC,CAAC;AAE9E,wBAAgB,oCAAoC,IAAI,MAAM,GAAG,IAAI,CAOpE;AAYD;;;GAGG;AACH,wBAAsB,mCAAmC,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAmBhG;AAED;kFACkF;AAClF,wBAAgB,8BAA8B,CAAC,CAAC,SAAS;IAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAClF,IAAI,EAAE,CAAC,EAAE,GAAG,IAAI,GAAG,SAAS,GAC7B,CAAC,GAAG,IAAI,CAcV"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
import {readDefaultCharacterFileName}from'./characterSlotsPreference.js';/**
|
|
2
2
|
* Persisted preference for which installed character card is **active**
|
|
3
3
|
* across the toolbar + dashboard webviews (`character_list[].file_name` stem —
|
|
4
4
|
* e.g. `"Aria"`, `"Seraphina"`).
|
|
@@ -52,7 +52,8 @@ async function setPreferredActiveCharacterFileName(fileName) {
|
|
|
52
52
|
fileName: fileName?.trim() || null
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
|
-
/** Pick the character row that matches the stored preference; otherwise
|
|
55
|
+
/** Pick the character row that matches the stored preference; otherwise the
|
|
56
|
+
* default-slot character (see `characterSlotsPreference`); otherwise list[0]. */
|
|
56
57
|
function resolveActiveCharacterFromList(list) {
|
|
57
58
|
if (!list?.length) return null;
|
|
58
59
|
const pref = readPreferredActiveCharacterFileName();
|
|
@@ -60,5 +61,11 @@ function resolveActiveCharacterFromList(list) {
|
|
|
60
61
|
const match = list.find(c => c.file_name === pref);
|
|
61
62
|
if (match) return match;
|
|
62
63
|
}
|
|
64
|
+
// Fallback: the file_name assigned to the user's default slot (1–9).
|
|
65
|
+
const slotFile = readDefaultCharacterFileName();
|
|
66
|
+
if (slotFile) {
|
|
67
|
+
const match = list.find(c => c.file_name === slotFile);
|
|
68
|
+
if (match) return match;
|
|
69
|
+
}
|
|
63
70
|
return list[0] ?? null;
|
|
64
71
|
}export{ACTIVE_CHARACTER_CHANGE_EVENT,ACTIVE_CHARACTER_FILE_NAME_STORAGE_KEY,readPreferredActiveCharacterFileName,resolveActiveCharacterFromList,setPreferredActiveCharacterFileName};//# sourceMappingURL=activeCharacterPreference.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"activeCharacterPreference.js","sources":["../../src/hooks/activeCharacterPreference.ts"],"sourcesContent":[null],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"activeCharacterPreference.js","sources":["../../src/hooks/activeCharacterPreference.ts"],"sourcesContent":[null],"names":[],"mappings":"yEAAA;;;;;;;;AAQG;AAKH;AAEA,MAAA,sCAAA,GAAA;AACA;AAEA,MAAA,6BAAgB,GAAA;AAmBhB,SAAA,oCAAA,GAAA;;;AAGG,IAAA,OAAA,MAAA,CAAA,YAAA,CAAA,OAAA,CAAA,sCAAA,CAAA;AACH,EAAA,CAAA,CAAA,MAAA;AAqBA,IAAA,OAAA,IAAA;AACkF,EAAA;AAClF;AAA2D,eAAU,QAAQ,CAAA,KAAO,EAAA,OAAA,EAAA;AAAE,EAAA,IAClF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-character overrides (Phase C):
|
|
3
|
+
* - `voiceId` → preferred cloud-TTS voice id (e.g. ElevenLabs voice id)
|
|
4
|
+
* - `addendum` → extra system-prompt text appended to the persona
|
|
5
|
+
*
|
|
6
|
+
* Storage keys (per character `file_name`):
|
|
7
|
+
* `yantra:character-voice-id:<file_name>`
|
|
8
|
+
* `yantra:character-addendum:<file_name>`
|
|
9
|
+
*
|
|
10
|
+
* Mirrors `characterSlotsPreference.ts`: localStorage + same-tab DOM event +
|
|
11
|
+
* cross-webview Tauri event so any panel reflects edits immediately.
|
|
12
|
+
*/
|
|
13
|
+
export declare const CHARACTER_OVERRIDES_CHANGE_EVENT = "yantra:character-overrides-change";
|
|
14
|
+
export interface CharacterOverrides {
|
|
15
|
+
voiceId: string;
|
|
16
|
+
addendum: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function readCharacterVoiceId(fileName: string): string;
|
|
19
|
+
export declare function readCharacterAddendum(fileName: string): string;
|
|
20
|
+
export declare function readCharacterOverrides(fileName: string): CharacterOverrides;
|
|
21
|
+
export declare function setCharacterVoiceId(fileName: string, voiceId: string): void;
|
|
22
|
+
export declare function setCharacterAddendum(fileName: string, addendum: string): void;
|
|
23
|
+
export declare function setCharacterOverrides(fileName: string, next: Partial<CharacterOverrides>): void;
|
|
24
|
+
/**
|
|
25
|
+
* Subscribe to overrides for a single character `file_name`. Updates on
|
|
26
|
+
* same-tab events, cross-tab `storage` events, and Tauri cross-webview emits.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useCharacterOverrides(fileName: string | null | undefined): CharacterOverrides;
|
|
29
|
+
//# sourceMappingURL=characterOverrides.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"characterOverrides.d.ts","sourceRoot":"","sources":["../../src/hooks/characterOverrides.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,eAAO,MAAM,gCAAgC,sCAAsC,CAAC;AAKpF,MAAM,WAAW,kBAAkB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;CACpB;AA2CD,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG9D;AAED,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAM3E;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAI3E;AAED,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAI7E;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,IAAI,CAK/F;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,kBAAkB,CA8C7F"}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import*as React from'react';/**
|
|
2
|
+
* Per-character overrides (Phase C):
|
|
3
|
+
* - `voiceId` → preferred cloud-TTS voice id (e.g. ElevenLabs voice id)
|
|
4
|
+
* - `addendum` → extra system-prompt text appended to the persona
|
|
5
|
+
*
|
|
6
|
+
* Storage keys (per character `file_name`):
|
|
7
|
+
* `yantra:character-voice-id:<file_name>`
|
|
8
|
+
* `yantra:character-addendum:<file_name>`
|
|
9
|
+
*
|
|
10
|
+
* Mirrors `characterSlotsPreference.ts`: localStorage + same-tab DOM event +
|
|
11
|
+
* cross-webview Tauri event so any panel reflects edits immediately.
|
|
12
|
+
*/
|
|
13
|
+
const CHARACTER_OVERRIDES_CHANGE_EVENT = 'yantra:character-overrides-change';
|
|
14
|
+
const VOICE_ID_PREFIX = 'yantra:character-voice-id:';
|
|
15
|
+
const ADDENDUM_PREFIX = 'yantra:character-addendum:';
|
|
16
|
+
const EMPTY = {
|
|
17
|
+
voiceId: '',
|
|
18
|
+
addendum: ''
|
|
19
|
+
};
|
|
20
|
+
function safeGet(key) {
|
|
21
|
+
if (typeof window === 'undefined') return '';
|
|
22
|
+
try {
|
|
23
|
+
return window.localStorage.getItem(key) ?? '';
|
|
24
|
+
} catch {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function safeSet(key, value) {
|
|
29
|
+
if (typeof window === 'undefined') return;
|
|
30
|
+
try {
|
|
31
|
+
if (value) window.localStorage.setItem(key, value);else window.localStorage.removeItem(key);
|
|
32
|
+
} catch {
|
|
33
|
+
/* quota */
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function safeEmit(event, payload) {
|
|
37
|
+
try {
|
|
38
|
+
// @ts-ignore optional peer
|
|
39
|
+
const mod = await import('@tauri-apps/api/event');
|
|
40
|
+
await mod.emit(event, payload);
|
|
41
|
+
} catch {
|
|
42
|
+
/* non-Tauri */
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function broadcast(fileName) {
|
|
46
|
+
if (typeof window === 'undefined') return;
|
|
47
|
+
window.dispatchEvent(new CustomEvent(CHARACTER_OVERRIDES_CHANGE_EVENT, {
|
|
48
|
+
detail: {
|
|
49
|
+
fileName
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
void safeEmit(CHARACTER_OVERRIDES_CHANGE_EVENT, {
|
|
53
|
+
fileName
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
function readCharacterVoiceId(fileName) {
|
|
57
|
+
if (!fileName) return '';
|
|
58
|
+
return safeGet(VOICE_ID_PREFIX + fileName).trim();
|
|
59
|
+
}
|
|
60
|
+
function readCharacterAddendum(fileName) {
|
|
61
|
+
if (!fileName) return '';
|
|
62
|
+
return safeGet(ADDENDUM_PREFIX + fileName);
|
|
63
|
+
}
|
|
64
|
+
function readCharacterOverrides(fileName) {
|
|
65
|
+
if (!fileName) return EMPTY;
|
|
66
|
+
return {
|
|
67
|
+
voiceId: readCharacterVoiceId(fileName),
|
|
68
|
+
addendum: readCharacterAddendum(fileName)
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function setCharacterVoiceId(fileName, voiceId) {
|
|
72
|
+
if (!fileName) return;
|
|
73
|
+
safeSet(VOICE_ID_PREFIX + fileName, voiceId.trim());
|
|
74
|
+
broadcast(fileName);
|
|
75
|
+
}
|
|
76
|
+
function setCharacterAddendum(fileName, addendum) {
|
|
77
|
+
if (!fileName) return;
|
|
78
|
+
safeSet(ADDENDUM_PREFIX + fileName, addendum);
|
|
79
|
+
broadcast(fileName);
|
|
80
|
+
}
|
|
81
|
+
function setCharacterOverrides(fileName, next) {
|
|
82
|
+
if (!fileName) return;
|
|
83
|
+
if (typeof next.voiceId === 'string') safeSet(VOICE_ID_PREFIX + fileName, next.voiceId.trim());
|
|
84
|
+
if (typeof next.addendum === 'string') safeSet(ADDENDUM_PREFIX + fileName, next.addendum);
|
|
85
|
+
broadcast(fileName);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Subscribe to overrides for a single character `file_name`. Updates on
|
|
89
|
+
* same-tab events, cross-tab `storage` events, and Tauri cross-webview emits.
|
|
90
|
+
*/
|
|
91
|
+
function useCharacterOverrides(fileName) {
|
|
92
|
+
const key = fileName ?? '';
|
|
93
|
+
const [snap, setSnap] = React.useState(() => readCharacterOverrides(key));
|
|
94
|
+
React.useEffect(() => {
|
|
95
|
+
if (typeof window === 'undefined') return;
|
|
96
|
+
setSnap(readCharacterOverrides(key));
|
|
97
|
+
if (!key) return;
|
|
98
|
+
const refresh = e => {
|
|
99
|
+
// Only re-read when our character (or any character) changed.
|
|
100
|
+
const detail = e?.detail;
|
|
101
|
+
if (detail?.fileName && detail.fileName !== key) return;
|
|
102
|
+
setSnap(readCharacterOverrides(key));
|
|
103
|
+
};
|
|
104
|
+
window.addEventListener(CHARACTER_OVERRIDES_CHANGE_EVENT, refresh);
|
|
105
|
+
window.addEventListener('storage', refresh);
|
|
106
|
+
let unlistenTauri = null;
|
|
107
|
+
let cancelled = false;
|
|
108
|
+
(async () => {
|
|
109
|
+
try {
|
|
110
|
+
// @ts-ignore optional peer
|
|
111
|
+
const mod = await import('@tauri-apps/api/event');
|
|
112
|
+
const off = await mod.listen(CHARACTER_OVERRIDES_CHANGE_EVENT, e => {
|
|
113
|
+
const fn = e.payload?.fileName;
|
|
114
|
+
if (fn && fn !== key) return;
|
|
115
|
+
setSnap(readCharacterOverrides(key));
|
|
116
|
+
});
|
|
117
|
+
if (cancelled) off();else unlistenTauri = off;
|
|
118
|
+
} catch {
|
|
119
|
+
/* non-Tauri */
|
|
120
|
+
}
|
|
121
|
+
})();
|
|
122
|
+
return () => {
|
|
123
|
+
cancelled = true;
|
|
124
|
+
window.removeEventListener(CHARACTER_OVERRIDES_CHANGE_EVENT, refresh);
|
|
125
|
+
window.removeEventListener('storage', refresh);
|
|
126
|
+
if (unlistenTauri) unlistenTauri();
|
|
127
|
+
};
|
|
128
|
+
}, [key]);
|
|
129
|
+
return snap;
|
|
130
|
+
}export{CHARACTER_OVERRIDES_CHANGE_EVENT,readCharacterAddendum,readCharacterOverrides,readCharacterVoiceId,setCharacterAddendum,setCharacterOverrides,setCharacterVoiceId,useCharacterOverrides};//# sourceMappingURL=characterOverrides.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"characterOverrides.js","sources":["../../src/hooks/characterOverrides.ts"],"sourcesContent":[null],"names":[],"mappings":"4BAAA;;;;;;;;;;;AAWG;sCASgC,GAAA;MAC/B,eAAgB,GAAA,4BAAA;MAChB,eAAiB,GAAA,4BAAA;AACpB,MAAA,KAAA,GAAA;AA2CD,EAAA,OAAA,EAAA,EAAA;AAKA,EAAA,QAAA,EAAA;AAKA,CAAA;AAQA,SAAA,OAAA,CAAA,GAAA,EAAA;AAMA,EAAA,IAAA,OAAA,MAAA,KAAgB,WAAA,EAAA,OAAoB,EAAC;AAMrC,EAAA,IAAA;AAOA,IAAA,OAAA,MAAA,CAAA,YAAA,CAAA,OAAA,CAAA,GAAA,CAAA,IAAA,EAAA;;;AAGG,EAAA;AACH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persisted character slots 1–9 — user-managed bookmarks of installed
|
|
3
|
+
* characters. Each slot points to a character `file_name` (e.g. `"Aria"`).
|
|
4
|
+
*
|
|
5
|
+
* One slot is marked **default** and is used at startup when no other active
|
|
6
|
+
* character preference is set (see `resolveActiveCharacterFromList`).
|
|
7
|
+
*
|
|
8
|
+
* Mirrors `activeCharacterPreference.ts`: localStorage + custom DOM event +
|
|
9
|
+
* Tauri broadcast so every webview reacts to changes.
|
|
10
|
+
*/
|
|
11
|
+
export declare const CHARACTER_SLOTS_STORAGE_KEY = "yantra:character-slots";
|
|
12
|
+
export declare const CHARACTER_SLOTS_DEFAULT_KEY = "yantra:character-slots:default";
|
|
13
|
+
export declare const CHARACTER_SLOTS_CHANGE_EVENT = "yantra:character-slots-change";
|
|
14
|
+
export type SlotNumber = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
|
|
15
|
+
export type CharacterSlots = Partial<Record<SlotNumber, string>>;
|
|
16
|
+
export declare const SLOT_NUMBERS: readonly SlotNumber[];
|
|
17
|
+
export declare function readCharacterSlots(): CharacterSlots;
|
|
18
|
+
export declare function readDefaultSlot(): SlotNumber;
|
|
19
|
+
export declare function writeCharacterSlots(next: CharacterSlots): Promise<void>;
|
|
20
|
+
export declare function setSlot(slot: SlotNumber, fileName: string | null): Promise<void>;
|
|
21
|
+
export declare function setDefaultSlot(slot: SlotNumber): Promise<void>;
|
|
22
|
+
/** Returns the file_name assigned to the default slot, if any. */
|
|
23
|
+
export declare function readDefaultCharacterFileName(): string | null;
|
|
24
|
+
export interface UseCharacterSlotsResult {
|
|
25
|
+
slots: CharacterSlots;
|
|
26
|
+
defaultSlot: SlotNumber;
|
|
27
|
+
setSlot: (slot: SlotNumber, fileName: string | null) => Promise<void>;
|
|
28
|
+
setDefaultSlot: (slot: SlotNumber) => Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
/** Subscribe to the persisted slot map + default slot. Updates on
|
|
31
|
+
* same-tab events, cross-tab `storage` events, and Tauri cross-webview emits. */
|
|
32
|
+
export declare function useCharacterSlots(): UseCharacterSlotsResult;
|
|
33
|
+
//# sourceMappingURL=characterSlotsPreference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"characterSlotsPreference.d.ts","sourceRoot":"","sources":["../../src/hooks/characterSlotsPreference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,eAAO,MAAM,2BAA2B,2BAA2B,CAAC;AACpE,eAAO,MAAM,2BAA2B,mCAAmC,CAAC;AAC5E,eAAO,MAAM,4BAA4B,kCAAkC,CAAC;AAE5E,MAAM,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAC3D,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;AAEjE,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAAyC,CAAC;AAMxF,wBAAgB,kBAAkB,IAAI,cAAc,CAiBnD;AAED,wBAAgB,eAAe,IAAI,UAAU,CAS5C;AAsBD,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7E;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAQtF;AAED,wBAAsB,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAQpE;AAED,kEAAkE;AAClE,wBAAgB,4BAA4B,IAAI,MAAM,GAAG,IAAI,CAI5D;AAMD,MAAM,WAAW,uBAAuB;IACpC,KAAK,EAAE,cAAc,CAAC;IACtB,WAAW,EAAE,UAAU,CAAC;IACxB,OAAO,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,cAAc,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAED;kFACkF;AAClF,wBAAgB,iBAAiB,IAAI,uBAAuB,CAyC3D"}
|