@hanzo/ui 8.29.22 → 8.29.23
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/dist/chat/Composer.cjs +181 -99
- package/dist/chat/Composer.d.ts +110 -81
- package/dist/chat/Composer.d.ts.map +1 -1
- package/dist/chat/Composer.js +183 -101
- package/dist/chat/Composer.js.map +1 -1
- package/dist/chat/Dial.cjs +96 -0
- package/dist/chat/Dial.d.ts +7 -0
- package/dist/chat/Dial.d.ts.map +1 -0
- package/dist/chat/Dial.js +94 -0
- package/dist/chat/Dial.js.map +1 -0
- package/dist/chat/Dictation.cjs +142 -0
- package/dist/chat/Dictation.d.ts +13 -0
- package/dist/chat/Dictation.d.ts.map +1 -0
- package/dist/chat/Dictation.js +140 -0
- package/dist/chat/Dictation.js.map +1 -0
- package/dist/chat/Families.cjs +41 -0
- package/dist/chat/Families.d.ts +3 -0
- package/dist/chat/Families.d.ts.map +1 -0
- package/dist/chat/Families.js +39 -0
- package/dist/chat/Families.js.map +1 -0
- package/dist/chat/Mention.cjs +25 -0
- package/dist/chat/Mention.d.ts +16 -0
- package/dist/chat/Mention.d.ts.map +1 -0
- package/dist/chat/Mention.js +22 -0
- package/dist/chat/Mention.js.map +1 -0
- package/dist/chat/Palette.cjs +22 -0
- package/dist/chat/Palette.d.ts +21 -0
- package/dist/chat/Palette.d.ts.map +1 -0
- package/dist/chat/Palette.js +20 -0
- package/dist/chat/Palette.js.map +1 -0
- package/dist/chat/Plus.cjs +76 -0
- package/dist/chat/Plus.d.ts +49 -0
- package/dist/chat/Plus.d.ts.map +1 -0
- package/dist/chat/Plus.js +74 -0
- package/dist/chat/Plus.js.map +1 -0
- package/dist/chat/Tune.cjs +55 -0
- package/dist/chat/Tune.d.ts +49 -0
- package/dist/chat/Tune.d.ts.map +1 -0
- package/dist/chat/Tune.js +51 -0
- package/dist/chat/Tune.js.map +1 -0
- package/dist/chat/index.cjs +25 -4
- package/dist/chat/index.d.ts +11 -3
- package/dist/chat/index.d.ts.map +1 -1
- package/dist/chat/index.js +10 -2
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/languages.cjs +17 -0
- package/dist/chat/languages.d.ts +11 -0
- package/dist/chat/languages.d.ts.map +1 -0
- package/dist/chat/languages.js +15 -0
- package/dist/chat/languages.js.map +1 -0
- package/dist/chat/pure.cjs +7 -1
- package/dist/chat/pure.d.ts +3 -1
- package/dist/chat/pure.d.ts.map +1 -1
- package/dist/chat/pure.js +4 -0
- package/dist/chat/pure.js.map +1 -1
- package/dist/chat/ring.cjs +9 -0
- package/dist/chat/ring.d.ts +11 -0
- package/dist/chat/ring.d.ts.map +1 -0
- package/dist/chat/ring.js +7 -0
- package/dist/chat/ring.js.map +1 -0
- package/dist/chat/send.cjs +3 -2
- package/dist/chat/send.d.ts +4 -1
- package/dist/chat/send.d.ts.map +1 -1
- package/dist/chat/send.js +3 -2
- package/dist/chat/send.js.map +1 -1
- package/dist/chat/ways.cjs +19 -0
- package/dist/chat/ways.d.ts +11 -0
- package/dist/chat/ways.d.ts.map +1 -0
- package/dist/chat/ways.js +17 -0
- package/dist/chat/ways.js.map +1 -0
- package/dist/gallery.cjs +4 -1
- package/dist/gallery.d.ts.map +1 -1
- package/dist/gallery.html +41 -15
- package/dist/gallery.js +5 -2
- package/dist/gallery.js.map +1 -1
- package/dist/models/ModelPicker.cjs +3 -3
- package/dist/models/ModelPicker.d.ts +6 -1
- package/dist/models/ModelPicker.d.ts.map +1 -1
- package/dist/models/ModelPicker.js +3 -3
- package/dist/models/ModelPicker.js.map +1 -1
- package/dist/models/ModelPickerMenu.cjs +19 -13
- package/dist/models/ModelPickerMenu.d.ts +4 -0
- package/dist/models/ModelPickerMenu.d.ts.map +1 -1
- package/dist/models/ModelPickerMenu.js +19 -13
- package/dist/models/ModelPickerMenu.js.map +1 -1
- package/dist/models/catalog.cjs +7 -0
- package/dist/models/catalog.d.ts +2 -0
- package/dist/models/catalog.d.ts.map +1 -1
- package/dist/models/catalog.js +6 -0
- package/dist/models/catalog.js.map +1 -1
- package/dist/models/index.cjs +2 -1
- package/dist/models/index.d.ts +1 -1
- package/dist/models/index.d.ts.map +1 -1
- package/dist/models/index.js +1 -1
- package/dist/models/index.js.map +1 -1
- package/dist/product/BranchSelect.cjs +1 -1
- package/dist/product/BranchSelect.js +1 -1
- package/dist/product/BranchSelect.js.map +1 -1
- package/dist/product/ChipSelect.cjs +60 -11
- package/dist/product/ChipSelect.d.ts +13 -2
- package/dist/product/ChipSelect.d.ts.map +1 -1
- package/dist/product/ChipSelect.js +61 -12
- package/dist/product/ChipSelect.js.map +1 -1
- package/dist/product/RepoSelect.cjs +11 -5
- package/dist/product/RepoSelect.d.ts +6 -2
- package/dist/product/RepoSelect.d.ts.map +1 -1
- package/dist/product/RepoSelect.js +13 -7
- package/dist/product/RepoSelect.js.map +1 -1
- package/dist/product/chipSelect.logic.d.ts +4 -1
- package/dist/product/chipSelect.logic.d.ts.map +1 -1
- package/dist/product/chipSelect.logic.js.map +1 -1
- package/dist/product/index.cjs +3 -3
- package/dist/product/index.d.ts +1 -1
- package/dist/product/index.d.ts.map +1 -1
- package/dist/product/index.js +3 -3
- package/dist/product/index.js.map +1 -1
- package/dist/styles.css +37 -11
- package/package.json +8 -2
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Dictation = Dictation;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
/**
|
|
7
|
+
* Dictation: the mic, and beside it a chevron that opens its menu — the
|
|
8
|
+
* microphones this browser has, the chosen one's live level, and the language
|
|
9
|
+
* dictation listens in (Auto is the browser's own).
|
|
10
|
+
*
|
|
11
|
+
* The chosen microphone is this browser's (a device id means nothing on
|
|
12
|
+
* another), kept in localStorage and handed to @hanzo/voice (`choose`), so
|
|
13
|
+
* every capture opens it; unchosen, the system's own input. The meter listens
|
|
14
|
+
* only while the menu is open, and only where the person has already let the
|
|
15
|
+
* page use a microphone: opening a menu never asks for permission.
|
|
16
|
+
*
|
|
17
|
+
* Its words are the reader's language (@hanzo/i18n `t`); a microphone's name
|
|
18
|
+
* is the device's and a language's is its own, drawn as given.
|
|
19
|
+
*/
|
|
20
|
+
const gui_1 = require("@hanzo/gui");
|
|
21
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
22
|
+
const composer_1 = require("@hanzo/composer");
|
|
23
|
+
const react_1 = require("@hanzo/i18n/react");
|
|
24
|
+
const gui_2 = require("../backends/gui/index.cjs");
|
|
25
|
+
const voice_1 = require("@hanzo/voice");
|
|
26
|
+
const react_2 = require("react");
|
|
27
|
+
const languages_1 = require("./languages.cjs");
|
|
28
|
+
const ring_1 = require("./ring.cjs");
|
|
29
|
+
const KEY = 'hanzo.mic';
|
|
30
|
+
function kept() {
|
|
31
|
+
try {
|
|
32
|
+
return window.localStorage.getItem(KEY);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function keep(id) {
|
|
39
|
+
try {
|
|
40
|
+
if (id)
|
|
41
|
+
window.localStorage.setItem(KEY, id);
|
|
42
|
+
else
|
|
43
|
+
window.localStorage.removeItem(KEY);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Kept for the page: `choose` holds it.
|
|
47
|
+
}
|
|
48
|
+
(0, voice_1.choose)(id);
|
|
49
|
+
}
|
|
50
|
+
// The page's choice reaches every capture from the first one.
|
|
51
|
+
if (typeof window !== 'undefined')
|
|
52
|
+
(0, voice_1.choose)(kept());
|
|
53
|
+
/** The mic is a plain mark until it is pointed at. */
|
|
54
|
+
const PLAIN = { bg: 'transparent', color: 'var(--foreground)', hoverStyle: { bg: '$hover' } };
|
|
55
|
+
/** The microphones this browser lists; labels only once the page may use one. */
|
|
56
|
+
function useInputs(open) {
|
|
57
|
+
const [inputs, setInputs] = (0, react_2.useState)([]);
|
|
58
|
+
const [allowed, setAllowed] = (0, react_2.useState)(false);
|
|
59
|
+
const [using, setUsing] = (0, react_2.useState)('');
|
|
60
|
+
(0, react_2.useEffect)(() => {
|
|
61
|
+
if (!open || typeof navigator === 'undefined' || !navigator.mediaDevices?.enumerateDevices)
|
|
62
|
+
return;
|
|
63
|
+
let live = true;
|
|
64
|
+
void navigator.mediaDevices.enumerateDevices().then((all) => {
|
|
65
|
+
if (!live)
|
|
66
|
+
return;
|
|
67
|
+
const mics = all.filter((d) => d.kind === 'audioinput' && d.deviceId !== 'default' && d.deviceId !== 'communications');
|
|
68
|
+
setInputs(mics);
|
|
69
|
+
// The system's own input: the group the browser's "default" entry belongs to, else the first.
|
|
70
|
+
setUsing(all.find((d) => d.kind === 'audioinput' && d.deviceId === 'default')?.groupId || mics[0]?.groupId || '');
|
|
71
|
+
setAllowed(mics.some((d) => d.label));
|
|
72
|
+
});
|
|
73
|
+
return () => {
|
|
74
|
+
live = false;
|
|
75
|
+
};
|
|
76
|
+
}, [open]);
|
|
77
|
+
return { inputs, allowed, using };
|
|
78
|
+
}
|
|
79
|
+
/** The chosen microphone's level while the menu is open, drawn without re-rendering. */
|
|
80
|
+
function Meter({ device, on }) {
|
|
81
|
+
// The level grows from the line's start: the right edge in a right-to-left language.
|
|
82
|
+
const { dir } = (0, react_1.useI18n)();
|
|
83
|
+
const bar = (0, react_2.useRef)(null);
|
|
84
|
+
(0, react_2.useEffect)(() => {
|
|
85
|
+
if (!on)
|
|
86
|
+
return;
|
|
87
|
+
let stream = null;
|
|
88
|
+
let ctx = null;
|
|
89
|
+
let frame = 0;
|
|
90
|
+
let live = true;
|
|
91
|
+
void navigator.mediaDevices
|
|
92
|
+
.getUserMedia({ audio: device ? { deviceId: { exact: device } } : true })
|
|
93
|
+
.then((s) => {
|
|
94
|
+
if (!live)
|
|
95
|
+
return s.getTracks().forEach((t) => t.stop());
|
|
96
|
+
stream = s;
|
|
97
|
+
ctx = new AudioContext();
|
|
98
|
+
const tap = ctx.createAnalyser();
|
|
99
|
+
tap.fftSize = 512;
|
|
100
|
+
ctx.createMediaStreamSource(s).connect(tap);
|
|
101
|
+
const buf = new Uint8Array(tap.fftSize);
|
|
102
|
+
const draw = () => {
|
|
103
|
+
tap.getByteTimeDomainData(buf);
|
|
104
|
+
let peak = 0;
|
|
105
|
+
for (const v of buf)
|
|
106
|
+
peak = Math.max(peak, Math.abs(v - 128));
|
|
107
|
+
if (bar.current)
|
|
108
|
+
bar.current.style.transform = `scaleX(${Math.min(1, (peak / 128) * 2.5)})`;
|
|
109
|
+
frame = requestAnimationFrame(draw);
|
|
110
|
+
};
|
|
111
|
+
draw();
|
|
112
|
+
})
|
|
113
|
+
.catch(() => { });
|
|
114
|
+
return () => {
|
|
115
|
+
live = false;
|
|
116
|
+
cancelAnimationFrame(frame);
|
|
117
|
+
stream?.getTracks().forEach((t) => t.stop());
|
|
118
|
+
void ctx?.close();
|
|
119
|
+
};
|
|
120
|
+
}, [device, on]);
|
|
121
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { items: "center", gap: 10, px: 8, py: 8, "data-slot": "mic-meter", "aria-hidden": true, children: [(0, jsx_runtime_1.jsx)(lucide_icons_2_1.Mic, { size: 14, color: "$hint" }), (0, jsx_runtime_1.jsx)(gui_1.YStack, { flex: 1, height: 4, rounded: 2, bg: "$hover", overflow: "hidden", children: (0, jsx_runtime_1.jsx)(gui_1.YStack, { ref: bar, height: 4, width: "100%", bg: "$ink",
|
|
122
|
+
// Empty at rest; the level is written per frame onto the node's transform, so no class is minted per value.
|
|
123
|
+
scaleX: 0, transformOrigin: dir === 'rtl' ? 'right' : 'left' }) })] }));
|
|
124
|
+
}
|
|
125
|
+
/** The mic and its chevron, one control on the composer's row. */
|
|
126
|
+
function Dictation({ voice, disabled = false, size = 36, language = null, onLanguage }) {
|
|
127
|
+
const { t } = (0, react_1.useI18n)();
|
|
128
|
+
const [open, setOpen] = (0, react_2.useState)(false);
|
|
129
|
+
// The Language list, drawn only while open (see Plus.tsx).
|
|
130
|
+
const [lang, setLang] = (0, react_2.useState)(false);
|
|
131
|
+
const { inputs, allowed, using } = useInputs(open);
|
|
132
|
+
const [device, setDevice] = (0, react_2.useState)(() => (typeof window === 'undefined' ? null : kept()));
|
|
133
|
+
const spoken = languages_1.LANGUAGES.find((l) => l.id === language);
|
|
134
|
+
// Two controls on one row, each its own box and its own focus ring: no frame around the pair.
|
|
135
|
+
return ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { "data-slot": "dictation", items: "center", shrink: 0, children: [(0, jsx_runtime_1.jsx)(composer_1.Control, { asChild: true, size: size, ...PLAIN, focusVisibleStyle: ring_1.RING, children: (0, jsx_runtime_1.jsx)(voice_1.Voice, { voice: voice, disabled: disabled, says: { idle: t('Dictate'), listening: t('Dictating — click to stop') }, children: (state) => (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Mic, { size: 16, color: "var(--foreground)", fill: state === 'idle' ? 'none' : 'var(--foreground)' }) }) }), (0, jsx_runtime_1.jsxs)(gui_2.DropdownMenu, { open: open, onOpenChange: setOpen, children: [(0, jsx_runtime_1.jsx)(gui_2.DropdownMenuTrigger, { asChild: true, disabled: disabled, children: (0, jsx_runtime_1.jsx)(gui_1.XStack, { render: "button", "aria-label": t('Dictation settings'), "data-slot": "dictation-menu", width: size, height: size, items: "center", justify: "center", rounded: 999, cursor: "pointer", bg: open ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, focusVisibleStyle: ring_1.RING, children: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.ChevronDown, { size: 14, color: "$soft" }) }) }), (0, jsx_runtime_1.jsxs)(gui_2.DropdownMenuContent, { align: "end", minW: 200, "data-slot": "dictation-panel", children: [(0, jsx_runtime_1.jsx)(Meter, { device: device, on: open && allowed }), inputs.length === 0 ? ((0, jsx_runtime_1.jsx)(gui_2.DropdownMenuLabel, { children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { fontSize: 14, color: "$hint", children: t('No devices found') }) })) : (inputs.map((d, i) => {
|
|
136
|
+
const on = device ? d.deviceId === device : d.groupId === using;
|
|
137
|
+
return ((0, jsx_runtime_1.jsx)(gui_2.DropdownMenuItem, { preventCloseOnSelect: true, onSelect: () => {
|
|
138
|
+
setDevice(d.deviceId);
|
|
139
|
+
keep(d.deviceId);
|
|
140
|
+
}, children: (0, jsx_runtime_1.jsxs)(gui_1.XStack, { flex: 1, items: "center", gap: 8, children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { fontSize: 14, color: "$ink", numberOfLines: 1, flex: 1, children: d.label || t('Microphone {n}', { n: i + 1 }) }), on ? (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Check, { size: 14, color: "var(--primary)", "aria-hidden": true }) : null] }) }, d.deviceId || i));
|
|
141
|
+
})), onLanguage ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(gui_2.DropdownMenuSeparator, {}), (0, jsx_runtime_1.jsx)(gui_2.DropdownMenuLabel, { children: (0, jsx_runtime_1.jsx)(gui_1.SizableText, { fontSize: 13, color: "$hint", children: t('Dictation settings') }) }), (0, jsx_runtime_1.jsxs)(gui_2.DropdownMenuSub, { open: lang, onOpenChange: setLang, children: [(0, jsx_runtime_1.jsx)(gui_2.DropdownMenuSubTrigger, { "data-slot": "dictation-language", onPress: () => setLang(true), children: (0, jsx_runtime_1.jsxs)(gui_1.XStack, { flex: 1, items: "center", gap: 8, children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { fontSize: 14, color: "$ink", flex: 1, children: t('Language') }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { fontSize: 14, color: "$hint", children: spoken?.label ?? t('Auto') })] }) }), lang ? ((0, jsx_runtime_1.jsx)(gui_2.DropdownMenuSubContent, { minW: 160, pointerEvents: "auto", children: [{ id: '', label: t('Auto') }, ...languages_1.LANGUAGES].map((l) => ((0, jsx_runtime_1.jsx)(gui_2.DropdownMenuItem, { onSelect: () => onLanguage(l.id || null), children: (0, jsx_runtime_1.jsxs)(gui_1.XStack, { flex: 1, items: "center", gap: 8, children: [(0, jsx_runtime_1.jsx)(gui_1.SizableText, { fontSize: 14, color: "$ink", flex: 1, children: l.label }), (language ?? '') === l.id ? (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Check, { size: 14, color: "var(--primary)", "aria-hidden": true }) : null] }) }, l.id || 'auto'))) })) : null] })] })) : null] })] })] }));
|
|
142
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type Machine } from '@hanzo/voice';
|
|
2
|
+
export interface DictationProps {
|
|
3
|
+
voice: Machine;
|
|
4
|
+
disabled?: boolean;
|
|
5
|
+
/** The box of each half, px: the composer's control size (36 on a laptop, 44 on a phone). */
|
|
6
|
+
size?: number;
|
|
7
|
+
/** The language dictation listens in, or null for Auto (the browser's own). */
|
|
8
|
+
language?: string | null;
|
|
9
|
+
onLanguage?: (id: string | null) => void;
|
|
10
|
+
}
|
|
11
|
+
/** The mic and its chevron, one control on the composer's row. */
|
|
12
|
+
export declare function Dictation({ voice, disabled, size, language, onLanguage }: DictationProps): import("react").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=Dictation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dictation.d.ts","sourceRoot":"","sources":["../../src/chat/Dictation.tsx"],"names":[],"mappings":"AA+BA,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AA+G1D,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,6FAA6F;IAC7F,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CACzC;AAED,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,QAAgB,EAAE,IAAS,EAAE,QAAe,EAAE,UAAU,EAAE,EAAE,cAAc,+BAyG5G"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* Dictation: the mic, and beside it a chevron that opens its menu — the
|
|
5
|
+
* microphones this browser has, the chosen one's live level, and the language
|
|
6
|
+
* dictation listens in (Auto is the browser's own).
|
|
7
|
+
*
|
|
8
|
+
* The chosen microphone is this browser's (a device id means nothing on
|
|
9
|
+
* another), kept in localStorage and handed to @hanzo/voice (`choose`), so
|
|
10
|
+
* every capture opens it; unchosen, the system's own input. The meter listens
|
|
11
|
+
* only while the menu is open, and only where the person has already let the
|
|
12
|
+
* page use a microphone: opening a menu never asks for permission.
|
|
13
|
+
*
|
|
14
|
+
* Its words are the reader's language (@hanzo/i18n `t`); a microphone's name
|
|
15
|
+
* is the device's and a language's is its own, drawn as given.
|
|
16
|
+
*/
|
|
17
|
+
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
18
|
+
import { Check, ChevronDown, Mic as MicIcon } from '@hanzogui/lucide-icons-2';
|
|
19
|
+
import { Control } from '@hanzo/composer';
|
|
20
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
21
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, } from '../backends/gui/index.js';
|
|
22
|
+
import { choose, Voice } from '@hanzo/voice';
|
|
23
|
+
import { useEffect, useRef, useState } from 'react';
|
|
24
|
+
import { LANGUAGES } from './languages.js';
|
|
25
|
+
import { RING } from './ring.js';
|
|
26
|
+
const KEY = 'hanzo.mic';
|
|
27
|
+
function kept() {
|
|
28
|
+
try {
|
|
29
|
+
return window.localStorage.getItem(KEY);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function keep(id) {
|
|
36
|
+
try {
|
|
37
|
+
if (id)
|
|
38
|
+
window.localStorage.setItem(KEY, id);
|
|
39
|
+
else
|
|
40
|
+
window.localStorage.removeItem(KEY);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Kept for the page: `choose` holds it.
|
|
44
|
+
}
|
|
45
|
+
choose(id);
|
|
46
|
+
}
|
|
47
|
+
// The page's choice reaches every capture from the first one.
|
|
48
|
+
if (typeof window !== 'undefined')
|
|
49
|
+
choose(kept());
|
|
50
|
+
/** The mic is a plain mark until it is pointed at. */
|
|
51
|
+
const PLAIN = { bg: 'transparent', color: 'var(--foreground)', hoverStyle: { bg: '$hover' } };
|
|
52
|
+
/** The microphones this browser lists; labels only once the page may use one. */
|
|
53
|
+
function useInputs(open) {
|
|
54
|
+
const [inputs, setInputs] = useState([]);
|
|
55
|
+
const [allowed, setAllowed] = useState(false);
|
|
56
|
+
const [using, setUsing] = useState('');
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (!open || typeof navigator === 'undefined' || !navigator.mediaDevices?.enumerateDevices)
|
|
59
|
+
return;
|
|
60
|
+
let live = true;
|
|
61
|
+
void navigator.mediaDevices.enumerateDevices().then((all) => {
|
|
62
|
+
if (!live)
|
|
63
|
+
return;
|
|
64
|
+
const mics = all.filter((d) => d.kind === 'audioinput' && d.deviceId !== 'default' && d.deviceId !== 'communications');
|
|
65
|
+
setInputs(mics);
|
|
66
|
+
// The system's own input: the group the browser's "default" entry belongs to, else the first.
|
|
67
|
+
setUsing(all.find((d) => d.kind === 'audioinput' && d.deviceId === 'default')?.groupId || mics[0]?.groupId || '');
|
|
68
|
+
setAllowed(mics.some((d) => d.label));
|
|
69
|
+
});
|
|
70
|
+
return () => {
|
|
71
|
+
live = false;
|
|
72
|
+
};
|
|
73
|
+
}, [open]);
|
|
74
|
+
return { inputs, allowed, using };
|
|
75
|
+
}
|
|
76
|
+
/** The chosen microphone's level while the menu is open, drawn without re-rendering. */
|
|
77
|
+
function Meter({ device, on }) {
|
|
78
|
+
// The level grows from the line's start: the right edge in a right-to-left language.
|
|
79
|
+
const { dir } = useI18n();
|
|
80
|
+
const bar = useRef(null);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!on)
|
|
83
|
+
return;
|
|
84
|
+
let stream = null;
|
|
85
|
+
let ctx = null;
|
|
86
|
+
let frame = 0;
|
|
87
|
+
let live = true;
|
|
88
|
+
void navigator.mediaDevices
|
|
89
|
+
.getUserMedia({ audio: device ? { deviceId: { exact: device } } : true })
|
|
90
|
+
.then((s) => {
|
|
91
|
+
if (!live)
|
|
92
|
+
return s.getTracks().forEach((t) => t.stop());
|
|
93
|
+
stream = s;
|
|
94
|
+
ctx = new AudioContext();
|
|
95
|
+
const tap = ctx.createAnalyser();
|
|
96
|
+
tap.fftSize = 512;
|
|
97
|
+
ctx.createMediaStreamSource(s).connect(tap);
|
|
98
|
+
const buf = new Uint8Array(tap.fftSize);
|
|
99
|
+
const draw = () => {
|
|
100
|
+
tap.getByteTimeDomainData(buf);
|
|
101
|
+
let peak = 0;
|
|
102
|
+
for (const v of buf)
|
|
103
|
+
peak = Math.max(peak, Math.abs(v - 128));
|
|
104
|
+
if (bar.current)
|
|
105
|
+
bar.current.style.transform = `scaleX(${Math.min(1, (peak / 128) * 2.5)})`;
|
|
106
|
+
frame = requestAnimationFrame(draw);
|
|
107
|
+
};
|
|
108
|
+
draw();
|
|
109
|
+
})
|
|
110
|
+
.catch(() => { });
|
|
111
|
+
return () => {
|
|
112
|
+
live = false;
|
|
113
|
+
cancelAnimationFrame(frame);
|
|
114
|
+
stream?.getTracks().forEach((t) => t.stop());
|
|
115
|
+
void ctx?.close();
|
|
116
|
+
};
|
|
117
|
+
}, [device, on]);
|
|
118
|
+
return (_jsxs(XStack, { items: "center", gap: 10, px: 8, py: 8, "data-slot": "mic-meter", "aria-hidden": true, children: [_jsx(MicIcon, { size: 14, color: "$hint" }), _jsx(YStack, { flex: 1, height: 4, rounded: 2, bg: "$hover", overflow: "hidden", children: _jsx(YStack, { ref: bar, height: 4, width: "100%", bg: "$ink",
|
|
119
|
+
// Empty at rest; the level is written per frame onto the node's transform, so no class is minted per value.
|
|
120
|
+
scaleX: 0, transformOrigin: dir === 'rtl' ? 'right' : 'left' }) })] }));
|
|
121
|
+
}
|
|
122
|
+
/** The mic and its chevron, one control on the composer's row. */
|
|
123
|
+
export function Dictation({ voice, disabled = false, size = 36, language = null, onLanguage }) {
|
|
124
|
+
const { t } = useI18n();
|
|
125
|
+
const [open, setOpen] = useState(false);
|
|
126
|
+
// The Language list, drawn only while open (see Plus.tsx).
|
|
127
|
+
const [lang, setLang] = useState(false);
|
|
128
|
+
const { inputs, allowed, using } = useInputs(open);
|
|
129
|
+
const [device, setDevice] = useState(() => (typeof window === 'undefined' ? null : kept()));
|
|
130
|
+
const spoken = LANGUAGES.find((l) => l.id === language);
|
|
131
|
+
// Two controls on one row, each its own box and its own focus ring: no frame around the pair.
|
|
132
|
+
return (_jsxs(XStack, { "data-slot": "dictation", items: "center", shrink: 0, children: [_jsx(Control, { asChild: true, size: size, ...PLAIN, focusVisibleStyle: RING, children: _jsx(Voice, { voice: voice, disabled: disabled, says: { idle: t('Dictate'), listening: t('Dictating — click to stop') }, children: (state) => _jsx(MicIcon, { size: 16, color: "var(--foreground)", fill: state === 'idle' ? 'none' : 'var(--foreground)' }) }) }), _jsxs(DropdownMenu, { open: open, onOpenChange: setOpen, children: [_jsx(DropdownMenuTrigger, { asChild: true, disabled: disabled, children: _jsx(XStack, { render: "button", "aria-label": t('Dictation settings'), "data-slot": "dictation-menu", width: size, height: size, items: "center", justify: "center", rounded: 999, cursor: "pointer", bg: open ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, focusVisibleStyle: RING, children: _jsx(ChevronDown, { size: 14, color: "$soft" }) }) }), _jsxs(DropdownMenuContent, { align: "end", minW: 200, "data-slot": "dictation-panel", children: [_jsx(Meter, { device: device, on: open && allowed }), inputs.length === 0 ? (_jsx(DropdownMenuLabel, { children: _jsx(SizableText, { fontSize: 14, color: "$hint", children: t('No devices found') }) })) : (inputs.map((d, i) => {
|
|
133
|
+
const on = device ? d.deviceId === device : d.groupId === using;
|
|
134
|
+
return (_jsx(DropdownMenuItem, { preventCloseOnSelect: true, onSelect: () => {
|
|
135
|
+
setDevice(d.deviceId);
|
|
136
|
+
keep(d.deviceId);
|
|
137
|
+
}, children: _jsxs(XStack, { flex: 1, items: "center", gap: 8, children: [_jsx(SizableText, { fontSize: 14, color: "$ink", numberOfLines: 1, flex: 1, children: d.label || t('Microphone {n}', { n: i + 1 }) }), on ? _jsx(Check, { size: 14, color: "var(--primary)", "aria-hidden": true }) : null] }) }, d.deviceId || i));
|
|
138
|
+
})), onLanguage ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsx(DropdownMenuLabel, { children: _jsx(SizableText, { fontSize: 13, color: "$hint", children: t('Dictation settings') }) }), _jsxs(DropdownMenuSub, { open: lang, onOpenChange: setLang, children: [_jsx(DropdownMenuSubTrigger, { "data-slot": "dictation-language", onPress: () => setLang(true), children: _jsxs(XStack, { flex: 1, items: "center", gap: 8, children: [_jsx(SizableText, { fontSize: 14, color: "$ink", flex: 1, children: t('Language') }), _jsx(SizableText, { fontSize: 14, color: "$hint", children: spoken?.label ?? t('Auto') })] }) }), lang ? (_jsx(DropdownMenuSubContent, { minW: 160, pointerEvents: "auto", children: [{ id: '', label: t('Auto') }, ...LANGUAGES].map((l) => (_jsx(DropdownMenuItem, { onSelect: () => onLanguage(l.id || null), children: _jsxs(XStack, { flex: 1, items: "center", gap: 8, children: [_jsx(SizableText, { fontSize: 14, color: "$ink", flex: 1, children: l.label }), (language ?? '') === l.id ? _jsx(Check, { size: 14, color: "var(--primary)", "aria-hidden": true }) : null] }) }, l.id || 'auto'))) })) : null] })] })) : null] })] })] }));
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=Dictation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Dictation.js","sourceRoot":"","sources":["../../src/chat/Dictation.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.useFamilies = useFamilies;
|
|
5
|
+
/**
|
|
6
|
+
* The model families as chips: Auto, then each of Hanzo's families the catalog
|
|
7
|
+
* lists (Enso's own tiers, Kai, Zen), one press each. Auto is the router, which
|
|
8
|
+
* picks the model for every message; a family chip names its first model the plan
|
|
9
|
+
* grants, and one the plan does not grant asks for the plan instead.
|
|
10
|
+
*
|
|
11
|
+
* Only what the catalog lists is drawn, so a person on a catalog without Kai sees
|
|
12
|
+
* no Kai. This is Chat's chip row, hidden until the person turns the chips on.
|
|
13
|
+
*/
|
|
14
|
+
const react_1 = require("@hanzo/i18n/react");
|
|
15
|
+
const react_2 = require("react");
|
|
16
|
+
const catalog_1 = require("../models/catalog.cjs");
|
|
17
|
+
/** The families a chip names, in order. Enso's router is Auto, so only a tier of Enso beside the router earns a chip. */
|
|
18
|
+
const NAMED = ['enso', 'kai', 'zen'];
|
|
19
|
+
function useFamilies(mind, onUpgrade) {
|
|
20
|
+
const { t } = (0, react_1.useI18n)();
|
|
21
|
+
return (0, react_2.useMemo)(() => {
|
|
22
|
+
const router = mind.models.find((m) => m.id === catalog_1.ENSO);
|
|
23
|
+
const rows = router
|
|
24
|
+
? [{ id: 'auto', label: t('Auto'), on: mind.model === catalog_1.ENSO, onPress: () => mind.pick(catalog_1.ENSO) }]
|
|
25
|
+
: [];
|
|
26
|
+
for (const id of NAMED) {
|
|
27
|
+
const own = (0, catalog_1.sortModels)(mind.models.filter((m) => m.family === id && m.id !== catalog_1.ENSO && m.access !== 'research'));
|
|
28
|
+
if (!own.length)
|
|
29
|
+
continue;
|
|
30
|
+
const first = own.find((m) => (0, catalog_1.grants)(mind.free, m.id, mind.models)) ?? own[0];
|
|
31
|
+
const label = catalog_1.FAMILIES.find((f) => f.id === id)?.label ?? id;
|
|
32
|
+
rows.push({
|
|
33
|
+
id,
|
|
34
|
+
label,
|
|
35
|
+
on: own.some((m) => m.id === mind.model),
|
|
36
|
+
onPress: () => ((0, catalog_1.grants)(mind.free, first.id, mind.models) ? mind.pick(first.id) : onUpgrade?.(first.id)),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return rows;
|
|
40
|
+
}, [mind, onUpgrade, t]);
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Families.d.ts","sourceRoot":"","sources":["../../src/chat/Families.tsx"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAKxC,wBAAgB,WAAW,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,EAAE,CAqBnF"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
/**
|
|
3
|
+
* The model families as chips: Auto, then each of Hanzo's families the catalog
|
|
4
|
+
* lists (Enso's own tiers, Kai, Zen), one press each. Auto is the router, which
|
|
5
|
+
* picks the model for every message; a family chip names its first model the plan
|
|
6
|
+
* grants, and one the plan does not grant asks for the plan instead.
|
|
7
|
+
*
|
|
8
|
+
* Only what the catalog lists is drawn, so a person on a catalog without Kai sees
|
|
9
|
+
* no Kai. This is Chat's chip row, hidden until the person turns the chips on.
|
|
10
|
+
*/
|
|
11
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
12
|
+
import { useMemo } from 'react';
|
|
13
|
+
import { ENSO, FAMILIES, grants, sortModels } from '../models/catalog.js';
|
|
14
|
+
/** The families a chip names, in order. Enso's router is Auto, so only a tier of Enso beside the router earns a chip. */
|
|
15
|
+
const NAMED = ['enso', 'kai', 'zen'];
|
|
16
|
+
export function useFamilies(mind, onUpgrade) {
|
|
17
|
+
const { t } = useI18n();
|
|
18
|
+
return useMemo(() => {
|
|
19
|
+
const router = mind.models.find((m) => m.id === ENSO);
|
|
20
|
+
const rows = router
|
|
21
|
+
? [{ id: 'auto', label: t('Auto'), on: mind.model === ENSO, onPress: () => mind.pick(ENSO) }]
|
|
22
|
+
: [];
|
|
23
|
+
for (const id of NAMED) {
|
|
24
|
+
const own = sortModels(mind.models.filter((m) => m.family === id && m.id !== ENSO && m.access !== 'research'));
|
|
25
|
+
if (!own.length)
|
|
26
|
+
continue;
|
|
27
|
+
const first = own.find((m) => grants(mind.free, m.id, mind.models)) ?? own[0];
|
|
28
|
+
const label = FAMILIES.find((f) => f.id === id)?.label ?? id;
|
|
29
|
+
rows.push({
|
|
30
|
+
id,
|
|
31
|
+
label,
|
|
32
|
+
on: own.some((m) => m.id === mind.model),
|
|
33
|
+
onPress: () => (grants(mind.free, first.id, mind.models) ? mind.pick(first.id) : onUpgrade?.(first.id)),
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return rows;
|
|
37
|
+
}, [mind, onUpgrade, t]);
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=Families.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Families.js","sourceRoot":"","sources":["../../src/chat/Families.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ;;;;;;;;GAQG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAE/B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAA;AAGxF,yHAAyH;AACzH,MAAM,KAAK,GAA2B,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAA;AAE5D,MAAM,UAAU,WAAW,CAAC,IAAU,EAAE,SAAmC;IACzE,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,CAAA;IACvB,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,CAAA;QACrD,MAAM,IAAI,GAAW,MAAM;YACzB,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,KAAK,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7F,CAAC,CAAC,EAAE,CAAA;QACN,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,EAAE,IAAI,CAAC,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAA;YAC9G,IAAI,CAAC,GAAG,CAAC,MAAM;gBAAE,SAAQ;YACzB,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAE,CAAA;YAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,IAAI,EAAE,CAAA;YAC5D,IAAI,CAAC,IAAI,CAAC;gBACR,EAAE;gBACF,KAAK;gBACL,EAAE,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,KAAK,CAAC;gBACxC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;aACxG,CAAC,CAAA;QACJ,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,CAAA;AAC1B,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.trigger = trigger;
|
|
5
|
+
exports.Mention = Mention;
|
|
6
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
7
|
+
/**
|
|
8
|
+
* `/` and `@` in the draft: the word the caret ends on opens a shelf — the
|
|
9
|
+
* skills for `/`, the plugins for `@` — in a list just above the frame, filtered
|
|
10
|
+
* by what follows the mark. The composer owns which row is lit and the keys that
|
|
11
|
+
* move it, so no surface carries that state.
|
|
12
|
+
*/
|
|
13
|
+
const gui_1 = require("@hanzo/gui");
|
|
14
|
+
const react_1 = require("@hanzo/i18n/react");
|
|
15
|
+
/** The `/` or `@` the caret ends on, and the words typed after it. */
|
|
16
|
+
function trigger(value) {
|
|
17
|
+
const m = /(^|\s)([/@])([^\s/@]*)$/.exec(value);
|
|
18
|
+
return m ? { char: m[2], query: m[3].toLowerCase(), at: m.index + m[1].length } : null;
|
|
19
|
+
}
|
|
20
|
+
/** The list a mark opens, above the frame. */
|
|
21
|
+
function Mention({ shelf, rows, index, onPick, skills }) {
|
|
22
|
+
const { t } = (0, react_1.useI18n)();
|
|
23
|
+
const tag = t(skills ? 'Skill' : 'Plugin');
|
|
24
|
+
return ((0, jsx_runtime_1.jsx)(gui_1.YStack, { role: 'listbox', "aria-label": t(skills ? 'Skills' : 'Plugins'), "data-slot": "mention", width: "100%", maxH: 240, overflowY: "auto", overflowX: "hidden", p: 6, rounded: 12, borderWidth: 1, borderColor: "$borderColor", bg: "$background", "$max-sm": { rounded: 22 }, children: rows.length ? (rows.map((r, i) => ((0, jsx_runtime_1.jsxs)(gui_1.XStack, { role: "option", "aria-selected": i === index, onPress: () => onPick(r.id), items: "center", gap: 10, px: 8, height: 36, rounded: 8, bg: i === index ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, cursor: "pointer", "$max-sm": { height: 46 }, children: [r.icon ? ((0, jsx_runtime_1.jsx)(gui_1.XStack, { width: 16, items: "center", justify: "center", "aria-hidden": true, children: r.icon })) : null, (0, jsx_runtime_1.jsx)(gui_1.SizableText, { fontSize: 15, color: "$ink", numberOfLines: 1, flex: 1, style: { textAlign: 'start' }, children: r.label }), (0, jsx_runtime_1.jsx)(gui_1.SizableText, { fontSize: 13, color: "$hint", children: r.on ? t('On') : tag })] }, r.id)))) : ((0, jsx_runtime_1.jsx)(gui_1.SizableText, { fontSize: 14, color: "$hint", px: 8, py: 8, children: t(shelf.loading ? 'Reading…' : 'Nothing matches') })) }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Shelf } from './Plus.js';
|
|
2
|
+
/** The `/` or `@` the caret ends on, and the words typed after it. */
|
|
3
|
+
export declare function trigger(value: string): {
|
|
4
|
+
char: '/' | '@';
|
|
5
|
+
query: string;
|
|
6
|
+
at: number;
|
|
7
|
+
} | null;
|
|
8
|
+
/** The list a mark opens, above the frame. */
|
|
9
|
+
export declare function Mention({ shelf, rows, index, onPick, skills }: {
|
|
10
|
+
shelf: Shelf;
|
|
11
|
+
rows: Shelf['rows'];
|
|
12
|
+
index: number;
|
|
13
|
+
onPick: (id: string) => void;
|
|
14
|
+
skills: boolean;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=Mention.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Mention.d.ts","sourceRoot":"","sources":["../../src/chat/Mention.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAEnC,sEAAsE;AACtE,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAG5F;AAED,8CAA8C;AAC9C,wBAAgB,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,+BAyDlK"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* `/` and `@` in the draft: the word the caret ends on opens a shelf — the
|
|
5
|
+
* skills for `/`, the plugins for `@` — in a list just above the frame, filtered
|
|
6
|
+
* by what follows the mark. The composer owns which row is lit and the keys that
|
|
7
|
+
* move it, so no surface carries that state.
|
|
8
|
+
*/
|
|
9
|
+
import { SizableText, XStack, YStack } from '@hanzo/gui';
|
|
10
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
11
|
+
/** The `/` or `@` the caret ends on, and the words typed after it. */
|
|
12
|
+
export function trigger(value) {
|
|
13
|
+
const m = /(^|\s)([/@])([^\s/@]*)$/.exec(value);
|
|
14
|
+
return m ? { char: m[2], query: m[3].toLowerCase(), at: m.index + m[1].length } : null;
|
|
15
|
+
}
|
|
16
|
+
/** The list a mark opens, above the frame. */
|
|
17
|
+
export function Mention({ shelf, rows, index, onPick, skills }) {
|
|
18
|
+
const { t } = useI18n();
|
|
19
|
+
const tag = t(skills ? 'Skill' : 'Plugin');
|
|
20
|
+
return (_jsx(YStack, { role: 'listbox', "aria-label": t(skills ? 'Skills' : 'Plugins'), "data-slot": "mention", width: "100%", maxH: 240, overflowY: "auto", overflowX: "hidden", p: 6, rounded: 12, borderWidth: 1, borderColor: "$borderColor", bg: "$background", "$max-sm": { rounded: 22 }, children: rows.length ? (rows.map((r, i) => (_jsxs(XStack, { role: "option", "aria-selected": i === index, onPress: () => onPick(r.id), items: "center", gap: 10, px: 8, height: 36, rounded: 8, bg: i === index ? '$hover' : 'transparent', hoverStyle: { bg: '$hover' }, cursor: "pointer", "$max-sm": { height: 46 }, children: [r.icon ? (_jsx(XStack, { width: 16, items: "center", justify: "center", "aria-hidden": true, children: r.icon })) : null, _jsx(SizableText, { fontSize: 15, color: "$ink", numberOfLines: 1, flex: 1, style: { textAlign: 'start' }, children: r.label }), _jsx(SizableText, { fontSize: 13, color: "$hint", children: r.on ? t('On') : tag })] }, r.id)))) : (_jsx(SizableText, { fontSize: 14, color: "$hint", px: 8, py: 8, children: t(shelf.loading ? 'Reading…' : 'Nothing matches') })) }));
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=Mention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Mention.js","sourceRoot":"","sources":["../../src/chat/Mention.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use client';"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Palette = Palette;
|
|
5
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
6
|
+
/**
|
|
7
|
+
* The composer's commands: what a surface can do from where the draft is — run
|
|
8
|
+
* it, open the repository in an editor, go to its issues. A `ChipSelect` with
|
|
9
|
+
* no words on its chip, so the search, the arrows, Enter and Esc are the ones
|
|
10
|
+
* every pill already has. ⌘K (Ctrl+K) opens it from the field.
|
|
11
|
+
*
|
|
12
|
+
* A command's `keys` is what already does it elsewhere (⌘↵ sends), never a key
|
|
13
|
+
* this list binds: a shortcut shown and not bound is a promise the page breaks.
|
|
14
|
+
*/
|
|
15
|
+
const react_1 = require("@hanzo/i18n/react");
|
|
16
|
+
const lucide_icons_2_1 = require("@hanzogui/lucide-icons-2");
|
|
17
|
+
const ChipSelect_1 = require("../product/ChipSelect.cjs");
|
|
18
|
+
function Palette({ commands, open, onOpenChange, placement = 'top-end', size, disabled, }) {
|
|
19
|
+
const { t } = (0, react_1.useI18n)();
|
|
20
|
+
const items = commands.map((c) => ({ id: c.id, label: c.label, icon: c.icon, hint: c.keys, disabled: c.disabled, command: c }));
|
|
21
|
+
return ((0, jsx_runtime_1.jsx)(ChipSelect_1.ChipSelect, { name: t('Commands'), label: "", quiet: true, ...(size ? { box: size } : null), icon: (0, jsx_runtime_1.jsx)(lucide_icons_2_1.Sparkles, { size: 15, color: "$soft" }), items: items, onChange: (i) => i.command.run(), placeholder: t('Search commands…'), empty: t('Nothing matches'), placement: placement, width: 300, disabled: disabled, open: open, onOpenChange: onOpenChange }));
|
|
22
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { type ChipPlacement } from '../product/ChipSelect.js';
|
|
3
|
+
export interface Command {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
icon?: ReactNode;
|
|
7
|
+
/** The keys that do this elsewhere, as said on the row (`⌘↵`). */
|
|
8
|
+
keys?: string;
|
|
9
|
+
run: () => void;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export declare function Palette({ commands, open, onOpenChange, placement, size, disabled, }: {
|
|
13
|
+
commands: readonly Command[];
|
|
14
|
+
open: boolean;
|
|
15
|
+
onOpenChange: (open: boolean) => void;
|
|
16
|
+
placement?: ChipPlacement;
|
|
17
|
+
/** The button's box, px: the composer's control size. */
|
|
18
|
+
size?: number;
|
|
19
|
+
disabled?: boolean;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
//# sourceMappingURL=Palette.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Palette.d.ts","sourceRoot":"","sources":["../../src/chat/Palette.tsx"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAA;AAGtE,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,IAAI,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB;AAID,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,SAAqB,EACrB,IAAI,EACJ,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;IAC5B,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,CAAC,EAAE,aAAa,CAAA;IACzB,yDAAyD;IACzD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,+BAqBA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
/**
|
|
4
|
+
* The composer's commands: what a surface can do from where the draft is — run
|
|
5
|
+
* it, open the repository in an editor, go to its issues. A `ChipSelect` with
|
|
6
|
+
* no words on its chip, so the search, the arrows, Enter and Esc are the ones
|
|
7
|
+
* every pill already has. ⌘K (Ctrl+K) opens it from the field.
|
|
8
|
+
*
|
|
9
|
+
* A command's `keys` is what already does it elsewhere (⌘↵ sends), never a key
|
|
10
|
+
* this list binds: a shortcut shown and not bound is a promise the page breaks.
|
|
11
|
+
*/
|
|
12
|
+
import { useI18n } from '@hanzo/i18n/react';
|
|
13
|
+
import { Sparkles } from '@hanzogui/lucide-icons-2';
|
|
14
|
+
import { ChipSelect } from '../product/ChipSelect.js';
|
|
15
|
+
export function Palette({ commands, open, onOpenChange, placement = 'top-end', size, disabled, }) {
|
|
16
|
+
const { t } = useI18n();
|
|
17
|
+
const items = commands.map((c) => ({ id: c.id, label: c.label, icon: c.icon, hint: c.keys, disabled: c.disabled, command: c }));
|
|
18
|
+
return (_jsx(ChipSelect, { name: t('Commands'), label: "", quiet: true, ...(size ? { box: size } : null), icon: _jsx(Sparkles, { size: 15, color: "$soft" }), items: items, onChange: (i) => i.command.run(), placeholder: t('Search commands…'), empty: t('Nothing matches'), placement: placement, width: 300, disabled: disabled, open: open, onOpenChange: onOpenChange }));
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=Palette.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Palette.js","sourceRoot":"","sources":["../../src/chat/Palette.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ;;;;;;;;GAQG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AAGnD,OAAO,EAAE,UAAU,EAAsB,MAAM,uBAAuB,CAAA;AAetE,MAAM,UAAU,OAAO,CAAC,EACtB,QAAQ,EACR,IAAI,EACJ,YAAY,EACZ,SAAS,GAAG,SAAS,EACrB,IAAI,EACJ,QAAQ,GAST;IACC,MAAM,EAAE,CAAC,EAAE,GAAG,OAAO,EAAE,CAAA;IACvB,MAAM,KAAK,GAAU,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;IACtI,OAAO,CACL,KAAC,UAAU,IACT,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,EACnB,KAAK,EAAC,EAAE,EACR,KAAK,WACD,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EACjC,IAAI,EAAE,KAAC,QAAQ,IAAC,IAAI,EAAE,EAAE,EAAE,KAAK,EAAC,OAAO,GAAG,EAC1C,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,EAChC,WAAW,EAAE,CAAC,CAAC,kBAAkB,CAAC,EAClC,KAAK,EAAE,CAAC,CAAC,iBAAiB,CAAC,EAC3B,SAAS,EAAE,SAAS,EACpB,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,IAAI,EACV,YAAY,EAAE,YAAY,GAC1B,CACH,CAAA;AACH,CAAC"}
|