@happyvertical/smrt-ui 0.51.38 → 0.52.0
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/AGENTS.md +14 -2
- package/README.md +257 -5
- package/dist/actions/__tests__/swipe-dismiss.test.js +69 -0
- package/dist/actions/swipe-dismiss.d.ts +42 -0
- package/dist/actions/swipe-dismiss.d.ts.map +1 -0
- package/dist/actions/swipe-dismiss.js +69 -0
- package/dist/components/calendar/Calendar.svelte +5 -0
- package/dist/components/calendar/Calendar.svelte.d.ts.map +1 -1
- package/dist/components/calendar/CalendarView.svelte +1250 -0
- package/dist/components/calendar/CalendarView.svelte.d.ts +66 -0
- package/dist/components/calendar/CalendarView.svelte.d.ts.map +1 -0
- package/dist/components/calendar/DayView.svelte +5 -0
- package/dist/components/calendar/DayView.svelte.d.ts.map +1 -1
- package/dist/components/calendar/__tests__/CalendarView.test.js +226 -0
- package/dist/components/calendar/__tests__/calendar-model.test.js +138 -0
- package/dist/components/calendar/calendar-model.d.ts +178 -0
- package/dist/components/calendar/calendar-model.d.ts.map +1 -0
- package/dist/components/calendar/calendar-model.js +362 -0
- package/dist/components/calendar/index.d.ts +12 -1
- package/dist/components/calendar/index.d.ts.map +1 -1
- package/dist/components/calendar/index.js +11 -1
- package/dist/components/data/CollectionList.svelte +23 -5
- package/dist/components/data/CollectionList.svelte.d.ts +8 -2
- package/dist/components/data/CollectionList.svelte.d.ts.map +1 -1
- package/dist/components/data/DataTable.svelte +118 -40
- package/dist/components/data/DataTable.svelte.d.ts.map +1 -1
- package/dist/components/data/DataTableController.d.ts +4 -0
- package/dist/components/data/DataTableController.d.ts.map +1 -1
- package/dist/components/data/DataTableController.js +11 -4
- package/dist/components/data/ListSortSelect.svelte +121 -0
- package/dist/components/data/ListSortSelect.svelte.d.ts +22 -0
- package/dist/components/data/ListSortSelect.svelte.d.ts.map +1 -0
- package/dist/components/data/SortableHeader.svelte +169 -0
- package/dist/components/data/SortableHeader.svelte.d.ts +30 -0
- package/dist/components/data/SortableHeader.svelte.d.ts.map +1 -0
- package/dist/components/data/__tests__/CollectionList.test.js +37 -0
- package/dist/components/data/__tests__/DataTable.test.js +76 -0
- package/dist/components/data/__tests__/DataTableController.test.js +35 -0
- package/dist/components/data/__tests__/SortableHeader.test.js +101 -0
- package/dist/components/data/__tests__/link-step-surface.test.js +172 -0
- package/dist/components/data/__tests__/list-sort-state.svelte.test.js +71 -0
- package/dist/components/data/__tests__/list-sort.test.js +91 -0
- package/dist/components/data/__tests__/surface-navigation.test.js +121 -0
- package/dist/components/data/data-surface.d.ts +1 -0
- package/dist/components/data/data-surface.d.ts.map +1 -1
- package/dist/components/data/data-surface.js +1 -0
- package/dist/components/data/index.d.ts +6 -0
- package/dist/components/data/index.d.ts.map +1 -1
- package/dist/components/data/index.js +6 -0
- package/dist/components/data/link-surface.d.ts +79 -0
- package/dist/components/data/link-surface.d.ts.map +1 -0
- package/dist/components/data/link-surface.js +155 -0
- package/dist/components/data/list-sort-state.svelte.d.ts +39 -0
- package/dist/components/data/list-sort-state.svelte.d.ts.map +1 -0
- package/dist/components/data/list-sort-state.svelte.js +47 -0
- package/dist/components/data/list-sort.d.ts +94 -0
- package/dist/components/data/list-sort.d.ts.map +1 -0
- package/dist/components/data/list-sort.js +182 -0
- package/dist/components/data/step-surface.d.ts +73 -0
- package/dist/components/data/step-surface.d.ts.map +1 -0
- package/dist/components/data/step-surface.js +177 -0
- package/dist/components/data/surface-navigation.d.ts +49 -0
- package/dist/components/data/surface-navigation.d.ts.map +1 -0
- package/dist/components/data/surface-navigation.js +100 -0
- package/dist/components/data/types.d.ts +19 -0
- package/dist/components/data/types.d.ts.map +1 -1
- package/dist/components/feedback/PhoneSheet.svelte +224 -0
- package/dist/components/feedback/PhoneSheet.svelte.d.ts +35 -0
- package/dist/components/feedback/PhoneSheet.svelte.d.ts.map +1 -0
- package/dist/components/feedback/WorkingStrip.svelte +426 -0
- package/dist/components/feedback/WorkingStrip.svelte.d.ts +37 -0
- package/dist/components/feedback/WorkingStrip.svelte.d.ts.map +1 -0
- package/dist/components/feedback/__tests__/PhoneSheet.test.js +101 -0
- package/dist/components/feedback/__tests__/WorkingStrip.test.js +172 -0
- package/dist/components/feedback/index.d.ts +4 -0
- package/dist/components/feedback/index.d.ts.map +1 -1
- package/dist/components/feedback/index.js +5 -0
- package/dist/components/feedback/working-status.d.ts +34 -0
- package/dist/components/feedback/working-status.d.ts.map +1 -0
- package/dist/components/feedback/working-status.js +5 -0
- package/dist/components/forms/Checkbox.svelte +26 -2
- package/dist/components/forms/Combobox.svelte +68 -12
- package/dist/components/forms/Combobox.svelte.d.ts +6 -0
- package/dist/components/forms/Combobox.svelte.d.ts.map +1 -1
- package/dist/components/forms/DictationButton.svelte +128 -0
- package/dist/components/forms/DictationButton.svelte.d.ts +19 -0
- package/dist/components/forms/DictationButton.svelte.d.ts.map +1 -0
- package/dist/components/forms/DictationStatus.svelte +164 -0
- package/dist/components/forms/DictationStatus.svelte.d.ts +11 -0
- package/dist/components/forms/DictationStatus.svelte.d.ts.map +1 -0
- package/dist/components/forms/Fieldset.svelte +5 -1
- package/dist/components/forms/Fieldset.svelte.d.ts +2 -0
- package/dist/components/forms/Fieldset.svelte.d.ts.map +1 -1
- package/dist/components/forms/FormActionBar.svelte +84 -0
- package/dist/components/forms/FormActionBar.svelte.d.ts +23 -0
- package/dist/components/forms/FormActionBar.svelte.d.ts.map +1 -0
- package/dist/components/forms/Input.svelte +9 -0
- package/dist/components/forms/Radio.svelte +15 -4
- package/dist/components/forms/SearchInput.svelte +207 -0
- package/dist/components/forms/SearchInput.svelte.d.ts +22 -0
- package/dist/components/forms/SearchInput.svelte.d.ts.map +1 -0
- package/dist/components/forms/Select.svelte +9 -0
- package/dist/components/forms/StagedControlReview.svelte +21 -12
- package/dist/components/forms/StagedControlReview.svelte.d.ts.map +1 -1
- package/dist/components/forms/Switch.svelte +11 -5
- package/dist/components/forms/TagsInput.svelte +8 -1
- package/dist/components/forms/Textarea.svelte +8 -0
- package/dist/components/forms/__tests__/Combobox.test.js +87 -0
- package/dist/components/forms/__tests__/ControlHitArea.test.js +73 -0
- package/dist/components/forms/__tests__/DictationButton.test.js +176 -0
- package/dist/components/forms/__tests__/Fieldset.test.js +37 -0
- package/dist/components/forms/__tests__/FormActionBar.test.js +22 -0
- package/dist/components/forms/__tests__/SearchInput.test.js +92 -0
- package/dist/components/forms/__tests__/StagedControlReview.test.js +37 -3
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte +19 -0
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts +11 -0
- package/dist/components/forms/__tests__/combobox-labels.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte +29 -0
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts +8 -0
- package/dist/components/forms/__tests__/composite-registration.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/__tests__/control-interaction.test.js +19 -1
- package/dist/components/forms/__tests__/control-proposals.test.js +63 -0
- package/dist/components/forms/__tests__/dictation-recording.test.js +582 -0
- package/dist/components/forms/__tests__/dictation.test.js +486 -0
- package/dist/components/forms/__tests__/long-press.test.js +175 -0
- package/dist/components/forms/__tests__/place-picker.fixture.svelte +33 -0
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts +12 -0
- package/dist/components/forms/__tests__/place-picker.fixture.svelte.d.ts.map +1 -0
- package/dist/components/forms/audio-capture.d.ts +69 -0
- package/dist/components/forms/audio-capture.d.ts.map +1 -0
- package/dist/components/forms/audio-capture.js +193 -0
- package/dist/components/forms/control-interaction.d.ts +6 -0
- package/dist/components/forms/control-interaction.d.ts.map +1 -1
- package/dist/components/forms/control-interaction.js +12 -1
- package/dist/components/forms/control-proposals.d.ts +55 -0
- package/dist/components/forms/control-proposals.d.ts.map +1 -0
- package/dist/components/forms/control-proposals.js +188 -0
- package/dist/components/forms/dictation-transcribe.d.ts +36 -0
- package/dist/components/forms/dictation-transcribe.d.ts.map +1 -0
- package/dist/components/forms/dictation-transcribe.js +65 -0
- package/dist/components/forms/dictation.svelte.d.ts +200 -0
- package/dist/components/forms/dictation.svelte.d.ts.map +1 -0
- package/dist/components/forms/dictation.svelte.js +686 -0
- package/dist/components/forms/index.d.ts +14 -1
- package/dist/components/forms/index.d.ts.map +1 -1
- package/dist/components/forms/index.js +14 -1
- package/dist/components/forms/insert-text.d.ts +12 -0
- package/dist/components/forms/insert-text.d.ts.map +1 -0
- package/dist/components/forms/insert-text.js +38 -0
- package/dist/components/forms/long-press.d.ts +88 -0
- package/dist/components/forms/long-press.d.ts.map +1 -0
- package/dist/components/forms/long-press.js +213 -0
- package/dist/components/forms/ready-beep.d.ts +24 -0
- package/dist/components/forms/ready-beep.d.ts.map +1 -0
- package/dist/components/forms/ready-beep.js +82 -0
- package/dist/components/layout/PageHeader.svelte +151 -17
- package/dist/components/layout/PageHeader.svelte.d.ts +47 -7
- package/dist/components/layout/PageHeader.svelte.d.ts.map +1 -1
- package/dist/components/layout/__tests__/PageHeader.test.js +87 -1
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +1 -0
- package/dist/components/layout/page-header-context.d.ts +25 -0
- package/dist/components/layout/page-header-context.d.ts.map +1 -0
- package/dist/components/layout/page-header-context.js +16 -0
- package/dist/components/nav/Tabs.svelte +241 -0
- package/dist/components/nav/Tabs.svelte.d.ts +15 -0
- package/dist/components/nav/Tabs.svelte.d.ts.map +1 -1
- package/dist/components/nav/__tests__/LinkTabs.test.js +117 -0
- package/dist/components/nav/index.d.ts +1 -0
- package/dist/components/nav/index.d.ts.map +1 -1
- package/dist/components/nav/index.js +1 -0
- package/dist/components/nav/tabs-overflow.d.ts +12 -0
- package/dist/components/nav/tabs-overflow.d.ts.map +1 -0
- package/dist/components/nav/tabs-overflow.js +17 -0
- package/dist/components/nav/types.d.ts +8 -0
- package/dist/components/nav/types.d.ts.map +1 -1
- package/dist/components/ui/Button.svelte +7 -0
- package/dist/components/ui/Card.svelte +14 -5
- package/dist/components/ui/Dropdown.svelte +55 -1
- package/dist/components/ui/Dropdown.svelte.d.ts +8 -0
- package/dist/components/ui/Dropdown.svelte.d.ts.map +1 -1
- package/dist/components/ui/IconToggle.svelte +139 -0
- package/dist/components/ui/IconToggle.svelte.d.ts +32 -0
- package/dist/components/ui/IconToggle.svelte.d.ts.map +1 -0
- package/dist/components/ui/IconToggleGroup.svelte +56 -0
- package/dist/components/ui/IconToggleGroup.svelte.d.ts +21 -0
- package/dist/components/ui/IconToggleGroup.svelte.d.ts.map +1 -0
- package/dist/components/ui/Pagination.svelte +1 -1
- package/dist/components/ui/Popover.svelte +17 -1
- package/dist/components/ui/Popover.svelte.d.ts +8 -0
- package/dist/components/ui/Popover.svelte.d.ts.map +1 -1
- package/dist/components/ui/__tests__/IconToggle.test.js +59 -0
- package/dist/components/ui/__tests__/IconToggleGroup.test.js +30 -0
- package/dist/components/ui/__tests__/Popover.test.js +44 -0
- package/dist/components/ui/__tests__/dropdown-popover-icon.test.js +125 -0
- package/dist/components/ui/__tests__/phone-touch-targets.test.js +39 -0
- package/dist/components/ui/index.d.ts +2 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +2 -0
- package/dist/i18n/strings.ui.d.ts +41 -0
- package/dist/i18n/strings.ui.d.ts.map +1 -1
- package/dist/i18n/strings.ui.js +46 -0
- package/dist/svelte/__tests__/playground.test.js +1 -0
- package/dist/svelte/playground/CalendarPreview.svelte +140 -0
- package/dist/svelte/playground/CalendarPreview.svelte.d.ts +4 -0
- package/dist/svelte/playground/CalendarPreview.svelte.d.ts.map +1 -0
- package/dist/svelte/playground.d.ts.map +1 -1
- package/dist/svelte/playground.js +10 -0
- package/dist/types-generic.d.ts +2 -0
- package/dist/types-generic.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type Place = {
|
|
2
|
+
name: string;
|
|
3
|
+
latitude: number;
|
|
4
|
+
longitude: number;
|
|
5
|
+
};
|
|
6
|
+
interface Props {
|
|
7
|
+
value: Place | null;
|
|
8
|
+
}
|
|
9
|
+
declare const PlacePicker: import("svelte").Component<Props, {}, "value">;
|
|
10
|
+
type PlacePicker = ReturnType<typeof PlacePicker>;
|
|
11
|
+
export default PlacePicker;
|
|
12
|
+
//# sourceMappingURL=place-picker.fixture.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"place-picker.fixture.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/forms/__tests__/place-picker.fixture.svelte.ts"],"names":[],"mappings":"AAMA,KAAK,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC;AACnE,UAAU,KAAK;IACb,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAkCD,QAAA,MAAM,WAAW,gDAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Record a short spoken message with `MediaRecorder`, for dictation in
|
|
3
|
+
* browsers whose speech recognition is missing or broken (Firefox, Brave).
|
|
4
|
+
*
|
|
5
|
+
* The recording is compressed by the browser: WebM/Opus where supported
|
|
6
|
+
* (Chrome, Edge, Firefox, Brave), MP4/AAC on Safari. It stops by itself at
|
|
7
|
+
* `maxDurationMs` (the recording so far is kept) and fails when it grows past
|
|
8
|
+
* `maxBytes`, so a forgotten microphone cannot fill memory or exceed what the
|
|
9
|
+
* server accepts. The audio stays in memory; nothing is written or logged.
|
|
10
|
+
*/
|
|
11
|
+
/** MIME types tried in order; the first `MediaRecorder` supports wins. */
|
|
12
|
+
export declare const DICTATION_AUDIO_MIME_TYPES: readonly string[];
|
|
13
|
+
/** Default cap on a recording's length: 2 minutes. */
|
|
14
|
+
export declare const DICTATION_MAX_DURATION_MS = 120000;
|
|
15
|
+
/** Default cap on a recording's size: 10 MB. */
|
|
16
|
+
export declare const DICTATION_MAX_BYTES: number;
|
|
17
|
+
/** A finished recording. */
|
|
18
|
+
export interface DictationRecording {
|
|
19
|
+
audio: Blob;
|
|
20
|
+
/** The recording's MIME type, e.g. `audio/webm;codecs=opus`. */
|
|
21
|
+
mimeType: string;
|
|
22
|
+
/** How long it recorded, in milliseconds. */
|
|
23
|
+
durationMs: number;
|
|
24
|
+
/** Whether it stopped because it reached `maxDurationMs`. */
|
|
25
|
+
reachedTimeLimit: boolean;
|
|
26
|
+
}
|
|
27
|
+
/** Records one message. `Dictation` creates a fresh one per message. */
|
|
28
|
+
export interface DictationAudioCapture {
|
|
29
|
+
/** Opens the microphone and starts recording. Rejects when it cannot. */
|
|
30
|
+
start(): Promise<void>;
|
|
31
|
+
/** Stops recording and resolves with the audio. */
|
|
32
|
+
stop(): Promise<DictationRecording>;
|
|
33
|
+
/** Stops recording and throws the audio away. */
|
|
34
|
+
cancel(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Called when the recording stops by itself: `time` (reached
|
|
37
|
+
* `maxDurationMs`; `stop()` still returns the audio) or `size` (grew past
|
|
38
|
+
* `maxBytes`; `stop()` rejects with a `too-long` `DictationError`).
|
|
39
|
+
*/
|
|
40
|
+
onLimit(callback: (reason: 'time' | 'size') => void): () => void;
|
|
41
|
+
}
|
|
42
|
+
export interface DictationAudioCaptureOptions {
|
|
43
|
+
/** Default {@link DICTATION_MAX_DURATION_MS}. */
|
|
44
|
+
maxDurationMs?: number;
|
|
45
|
+
/** Default {@link DICTATION_MAX_BYTES}. */
|
|
46
|
+
maxBytes?: number;
|
|
47
|
+
/** MIME types to try, in order. Default {@link DICTATION_AUDIO_MIME_TYPES}. */
|
|
48
|
+
mimeTypes?: readonly string[];
|
|
49
|
+
/** Media constraints for `getUserMedia`. Default `{ audio: true }`. */
|
|
50
|
+
constraints?: MediaStreamConstraints;
|
|
51
|
+
}
|
|
52
|
+
/** Creates a recorder for one message. */
|
|
53
|
+
export type DictationAudioCaptureFactory = (options: DictationAudioCaptureOptions) => DictationAudioCapture;
|
|
54
|
+
/**
|
|
55
|
+
* An error that already knows which plain-words message it needs.
|
|
56
|
+
* `classifyDictationError` reads `dictationKind` first.
|
|
57
|
+
*/
|
|
58
|
+
export declare class DictationError extends Error {
|
|
59
|
+
readonly dictationKind: string;
|
|
60
|
+
readonly code: string;
|
|
61
|
+
constructor(dictationKind: string, message: string, code?: string);
|
|
62
|
+
}
|
|
63
|
+
/** Whether this browser can record audio for dictation. */
|
|
64
|
+
export declare function canCaptureDictationAudio(): boolean;
|
|
65
|
+
/** The first MIME type `MediaRecorder` can record, or `''` (its default). */
|
|
66
|
+
export declare function pickDictationMimeType(candidates?: readonly string[]): string;
|
|
67
|
+
/** Records with `MediaRecorder` (see the module comment). */
|
|
68
|
+
export declare const createMediaRecorderCapture: DictationAudioCaptureFactory;
|
|
69
|
+
//# sourceMappingURL=audio-capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-capture.d.ts","sourceRoot":"","sources":["../../../src/components/forms/audio-capture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,0EAA0E;AAC1E,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAKvD,CAAC;AAEF,sDAAsD;AACtD,eAAO,MAAM,yBAAyB,SAAU,CAAC;AAEjD,gDAAgD;AAChD,eAAO,MAAM,mBAAmB,QAAmB,CAAC;AAEpD,4BAA4B;AAC5B,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,IAAI,CAAC;IACZ,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,wEAAwE;AACxE,MAAM,WAAW,qBAAqB;IACpC,yEAAyE;IACzE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,mDAAmD;IACnD,IAAI,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACpC,iDAAiD;IACjD,MAAM,IAAI,IAAI,CAAC;IACf;;;;OAIG;IACH,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;CAClE;AAED,MAAM,WAAW,4BAA4B;IAC3C,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,uEAAuE;IACvE,WAAW,CAAC,EAAE,sBAAsB,CAAC;CACtC;AAED,0CAA0C;AAC1C,MAAM,MAAM,4BAA4B,GAAG,CACzC,OAAO,EAAE,4BAA4B,KAClC,qBAAqB,CAAC;AAK3B;;;GAGG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAMlE;AASD,2DAA2D;AAC3D,wBAAgB,wBAAwB,IAAI,OAAO,CAMlD;AAED,6EAA6E;AAC7E,wBAAgB,qBAAqB,CACnC,UAAU,GAAE,SAAS,MAAM,EAA+B,GACzD,MAAM,CAOR;AAED,6DAA6D;AAC7D,eAAO,MAAM,0BAA0B,EAAE,4BA+IxC,CAAC"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Record a short spoken message with `MediaRecorder`, for dictation in
|
|
3
|
+
* browsers whose speech recognition is missing or broken (Firefox, Brave).
|
|
4
|
+
*
|
|
5
|
+
* The recording is compressed by the browser: WebM/Opus where supported
|
|
6
|
+
* (Chrome, Edge, Firefox, Brave), MP4/AAC on Safari. It stops by itself at
|
|
7
|
+
* `maxDurationMs` (the recording so far is kept) and fails when it grows past
|
|
8
|
+
* `maxBytes`, so a forgotten microphone cannot fill memory or exceed what the
|
|
9
|
+
* server accepts. The audio stays in memory; nothing is written or logged.
|
|
10
|
+
*/
|
|
11
|
+
/** MIME types tried in order; the first `MediaRecorder` supports wins. */
|
|
12
|
+
export const DICTATION_AUDIO_MIME_TYPES = [
|
|
13
|
+
'audio/webm;codecs=opus',
|
|
14
|
+
'audio/webm',
|
|
15
|
+
'audio/mp4',
|
|
16
|
+
'audio/ogg;codecs=opus',
|
|
17
|
+
];
|
|
18
|
+
/** Default cap on a recording's length: 2 minutes. */
|
|
19
|
+
export const DICTATION_MAX_DURATION_MS = 120_000;
|
|
20
|
+
/** Default cap on a recording's size: 10 MB. */
|
|
21
|
+
export const DICTATION_MAX_BYTES = 10 * 1024 * 1024;
|
|
22
|
+
/**
|
|
23
|
+
* An error that already knows which plain-words message it needs.
|
|
24
|
+
* `classifyDictationError` reads `dictationKind` first.
|
|
25
|
+
*/
|
|
26
|
+
export class DictationError extends Error {
|
|
27
|
+
dictationKind;
|
|
28
|
+
code;
|
|
29
|
+
constructor(dictationKind, message, code) {
|
|
30
|
+
super(message);
|
|
31
|
+
this.name = 'DictationError';
|
|
32
|
+
this.dictationKind = dictationKind;
|
|
33
|
+
this.code = code ?? dictationKind;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** How long `stop()` waits for the recorder's `stop` event. */
|
|
37
|
+
const STOP_EVENT_TIMEOUT_MS = 3000;
|
|
38
|
+
function captureError(kind, message) {
|
|
39
|
+
return new DictationError(kind, message);
|
|
40
|
+
}
|
|
41
|
+
/** Whether this browser can record audio for dictation. */
|
|
42
|
+
export function canCaptureDictationAudio() {
|
|
43
|
+
return (typeof MediaRecorder !== 'undefined' &&
|
|
44
|
+
typeof navigator !== 'undefined' &&
|
|
45
|
+
typeof navigator.mediaDevices?.getUserMedia === 'function');
|
|
46
|
+
}
|
|
47
|
+
/** The first MIME type `MediaRecorder` can record, or `''` (its default). */
|
|
48
|
+
export function pickDictationMimeType(candidates = DICTATION_AUDIO_MIME_TYPES) {
|
|
49
|
+
if (typeof MediaRecorder === 'undefined')
|
|
50
|
+
return '';
|
|
51
|
+
const supports = typeof MediaRecorder.isTypeSupported === 'function'
|
|
52
|
+
? (type) => MediaRecorder.isTypeSupported(type)
|
|
53
|
+
: () => false;
|
|
54
|
+
return candidates.find((type) => supports(type)) ?? '';
|
|
55
|
+
}
|
|
56
|
+
/** Records with `MediaRecorder` (see the module comment). */
|
|
57
|
+
export const createMediaRecorderCapture = (options = {}) => {
|
|
58
|
+
const maxDurationMs = options.maxDurationMs ?? DICTATION_MAX_DURATION_MS;
|
|
59
|
+
const maxBytes = options.maxBytes ?? DICTATION_MAX_BYTES;
|
|
60
|
+
const limitListeners = new Set();
|
|
61
|
+
let stream = null;
|
|
62
|
+
let recorder = null;
|
|
63
|
+
let chunks = [];
|
|
64
|
+
let bytes = 0;
|
|
65
|
+
let startedAt = 0;
|
|
66
|
+
let endedAt = 0;
|
|
67
|
+
let timer = null;
|
|
68
|
+
let reachedTimeLimit = false;
|
|
69
|
+
let tooBig = false;
|
|
70
|
+
let cancelled = false;
|
|
71
|
+
let stopped = null;
|
|
72
|
+
let resolveStopped = null;
|
|
73
|
+
const emitLimit = (reason) => {
|
|
74
|
+
for (const listener of limitListeners) {
|
|
75
|
+
try {
|
|
76
|
+
listener(reason);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// A broken listener must not keep the microphone open.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const release = () => {
|
|
84
|
+
if (timer)
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
timer = null;
|
|
87
|
+
for (const track of stream?.getTracks() ?? [])
|
|
88
|
+
track.stop();
|
|
89
|
+
stream = null;
|
|
90
|
+
};
|
|
91
|
+
const halt = () => {
|
|
92
|
+
endedAt ||= Date.now();
|
|
93
|
+
if (recorder && recorder.state !== 'inactive') {
|
|
94
|
+
try {
|
|
95
|
+
recorder.stop();
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
resolveStopped?.();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
resolveStopped?.();
|
|
103
|
+
}
|
|
104
|
+
release();
|
|
105
|
+
};
|
|
106
|
+
return {
|
|
107
|
+
async start() {
|
|
108
|
+
if (!canCaptureDictationAudio()) {
|
|
109
|
+
throw captureError('unsupported', 'This browser cannot record audio (no MediaRecorder).');
|
|
110
|
+
}
|
|
111
|
+
stream = await navigator.mediaDevices.getUserMedia(options.constraints ?? { audio: true });
|
|
112
|
+
if (cancelled) {
|
|
113
|
+
release();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const mimeType = pickDictationMimeType(options.mimeTypes);
|
|
117
|
+
try {
|
|
118
|
+
recorder = mimeType
|
|
119
|
+
? new MediaRecorder(stream, { mimeType })
|
|
120
|
+
: new MediaRecorder(stream);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
release();
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
stopped = new Promise((resolve) => {
|
|
127
|
+
resolveStopped = resolve;
|
|
128
|
+
});
|
|
129
|
+
recorder.addEventListener('dataavailable', (event) => {
|
|
130
|
+
if (!event.data || event.data.size === 0 || cancelled)
|
|
131
|
+
return;
|
|
132
|
+
chunks.push(event.data);
|
|
133
|
+
bytes += event.data.size;
|
|
134
|
+
if (bytes > maxBytes && !tooBig) {
|
|
135
|
+
tooBig = true;
|
|
136
|
+
chunks = [];
|
|
137
|
+
emitLimit('size');
|
|
138
|
+
halt();
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
recorder.addEventListener('stop', () => resolveStopped?.());
|
|
142
|
+
recorder.addEventListener('error', () => halt());
|
|
143
|
+
// Hand over data every second so the size cap is checked as it grows.
|
|
144
|
+
recorder.start(1000);
|
|
145
|
+
startedAt = Date.now();
|
|
146
|
+
timer = setTimeout(() => {
|
|
147
|
+
reachedTimeLimit = true;
|
|
148
|
+
emitLimit('time');
|
|
149
|
+
halt();
|
|
150
|
+
}, maxDurationMs);
|
|
151
|
+
},
|
|
152
|
+
async stop() {
|
|
153
|
+
if (!recorder) {
|
|
154
|
+
release();
|
|
155
|
+
throw captureError('unsupported', 'Recording never started.');
|
|
156
|
+
}
|
|
157
|
+
halt();
|
|
158
|
+
// A browser that never fires the recorder's `stop` event must not leave
|
|
159
|
+
// the field stuck "writing it down": give up waiting and use the audio
|
|
160
|
+
// that has arrived.
|
|
161
|
+
let giveUp;
|
|
162
|
+
await Promise.race([
|
|
163
|
+
stopped,
|
|
164
|
+
new Promise((resolve) => {
|
|
165
|
+
giveUp = setTimeout(resolve, STOP_EVENT_TIMEOUT_MS);
|
|
166
|
+
}),
|
|
167
|
+
]);
|
|
168
|
+
if (giveUp)
|
|
169
|
+
clearTimeout(giveUp);
|
|
170
|
+
const type = recorder.mimeType || pickDictationMimeType(options.mimeTypes);
|
|
171
|
+
const recording = {
|
|
172
|
+
audio: new Blob(chunks, type ? { type } : undefined),
|
|
173
|
+
mimeType: type || 'audio/webm',
|
|
174
|
+
durationMs: Math.max(0, (endedAt || Date.now()) - startedAt),
|
|
175
|
+
reachedTimeLimit,
|
|
176
|
+
};
|
|
177
|
+
chunks = [];
|
|
178
|
+
if (tooBig || recording.audio.size > maxBytes) {
|
|
179
|
+
throw captureError('too-long', 'The recording is too large to send.');
|
|
180
|
+
}
|
|
181
|
+
return recording;
|
|
182
|
+
},
|
|
183
|
+
cancel() {
|
|
184
|
+
cancelled = true;
|
|
185
|
+
chunks = [];
|
|
186
|
+
halt();
|
|
187
|
+
},
|
|
188
|
+
onLimit(callback) {
|
|
189
|
+
limitListeners.add(callback);
|
|
190
|
+
return () => limitListeners.delete(callback);
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
};
|
|
@@ -54,6 +54,12 @@ export interface ControlMetadata {
|
|
|
54
54
|
options?: ControlOption[];
|
|
55
55
|
unit?: string;
|
|
56
56
|
capabilities?: ControlCapability[];
|
|
57
|
+
/**
|
|
58
|
+
* JSON Schema for this control's value, for composites whose value is not a
|
|
59
|
+
* plain string/number/boolean (a place picker's `{ name, latitude, … }`).
|
|
60
|
+
* Proposal adapters use it verbatim instead of deriving one from `kind`.
|
|
61
|
+
*/
|
|
62
|
+
valueSchema?: Record<string, unknown>;
|
|
57
63
|
}
|
|
58
64
|
export interface ControlRuntimeState {
|
|
59
65
|
disabled?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control-interaction.d.ts","sourceRoot":"","sources":["../../../src/components/forms/control-interaction.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,MAAM,WAAW,GACnB,MAAM,GACN,OAAO,GACP,UAAU,GACV,QAAQ,GACR,MAAM,GACN,MAAM,GACN,UAAU,GACV,UAAU,GACV,QAAQ,GACR,SAAS,GACT,UAAU,GACV,cAAc,GACd,YAAY,GACZ,UAAU,GACV,aAAa,GACb,QAAQ,GACR,eAAe,GACf,QAAQ,GACR,cAAc,GACd,mBAAmB,GACnB,MAAM,GACN,QAAQ,CAAC;AAEb,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEhF,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,OAAO,GACP,QAAQ,GACR,WAAW,GACX,SAAS,GACT,UAAU,GACV,OAAO,GACP,OAAO,GACP,SAAS,GACT,OAAO,GACP,MAAM,CAAC;AAEX,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,uEAAuE;AACvE,MAAM,WAAW,yBAAyB;IACxC,0EAA0E;IAC1E,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,kBAAkB,CAAC;IACjC,OAAO,CAAC,EAAE,aAAa,EAAE,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,iBAAiB,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"control-interaction.d.ts","sourceRoot":"","sources":["../../../src/components/forms/control-interaction.ts"],"names":[],"mappings":"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"}
|
|
@@ -489,6 +489,9 @@ export function createControlInteractionRegistry(options = {}) {
|
|
|
489
489
|
options: registration.metadata.options?.map((option) => ({
|
|
490
490
|
...option,
|
|
491
491
|
})),
|
|
492
|
+
...(registration.metadata.valueSchema
|
|
493
|
+
? { valueSchema: cloneValue(registration.metadata.valueSchema) }
|
|
494
|
+
: {}),
|
|
492
495
|
},
|
|
493
496
|
state: {
|
|
494
497
|
...runtimeState,
|
|
@@ -755,7 +758,15 @@ export function createControlInteractionRegistry(options = {}) {
|
|
|
755
758
|
}
|
|
756
759
|
switch (command.action) {
|
|
757
760
|
case 'focus':
|
|
758
|
-
|
|
761
|
+
// An agent never moves the person's keyboard focus: its `focus`
|
|
762
|
+
// shows the control instead (scroll into view and highlight).
|
|
763
|
+
if (context.source === 'agent') {
|
|
764
|
+
await registration.reveal?.();
|
|
765
|
+
await registration.highlight?.();
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
await registration.focus?.();
|
|
769
|
+
}
|
|
759
770
|
break;
|
|
760
771
|
case 'reveal':
|
|
761
772
|
await registration.reveal?.();
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form-level proposals over a control interaction registry.
|
|
3
|
+
*
|
|
4
|
+
* Transport-neutral helpers for "propose values for this form" adapters (the
|
|
5
|
+
* rich smrt-svelte `<Form webmcp>` tool, `FormScope`, a chat or voice
|
|
6
|
+
* adapter). They read whatever the registry holds for one `formId` — every
|
|
7
|
+
* smrt-ui primitive (`Input`, `Select`, `Textarea`, `Combobox`, …) and every
|
|
8
|
+
* composite registered through `useControlRegistration` — and only ever STAGE
|
|
9
|
+
* values: a person applies them through `StagedControlReview`.
|
|
10
|
+
*/
|
|
11
|
+
import type { ControlCommandSource, ControlInteractionRegistry, ControlSnapshot } from './control-interaction.js';
|
|
12
|
+
/** Tool-safe name (`[a-z0-9_]`) for a form's proposal tool. */
|
|
13
|
+
export declare function controlProposalToolName(formId: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a control can take a proposal at all: never secret or sensitive,
|
|
16
|
+
* never declared unwritable, disabled, or read-only, and never a file or
|
|
17
|
+
* password (neither is representable as a proposed JSON value).
|
|
18
|
+
*/
|
|
19
|
+
export declare function isControlProposable(snapshot: ControlSnapshot): boolean;
|
|
20
|
+
/** JSON Schema for one control's proposed value. */
|
|
21
|
+
export declare function controlProposalSchema(snapshot: ControlSnapshot): Record<string, unknown>;
|
|
22
|
+
export interface ControlProposalOptions {
|
|
23
|
+
/** Control ids another source already describes (skipped here). */
|
|
24
|
+
exclude?: Iterable<string>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The proposable controls of one form, keyed by control id. A
|
|
28
|
+
* record-qualified control (subject) shares its controlId with its siblings;
|
|
29
|
+
* the first registered wins so the schema key stays unambiguous.
|
|
30
|
+
*/
|
|
31
|
+
export declare function proposableControls(registry: ControlInteractionRegistry, formId: string, options?: ControlProposalOptions): Map<string, ControlSnapshot>;
|
|
32
|
+
/** `{ controlId: schema }` for every proposable control of a form. */
|
|
33
|
+
export declare function controlProposalProperties(registry: ControlInteractionRegistry, formId: string, options?: ControlProposalOptions): Record<string, Record<string, unknown>>;
|
|
34
|
+
/** Object schema with one optional property per proposable control. */
|
|
35
|
+
export declare function controlProposalInputSchema(registry: ControlInteractionRegistry, formId: string, options?: ControlProposalOptions): Record<string, unknown>;
|
|
36
|
+
export interface StageControlProposalsResult {
|
|
37
|
+
ok: boolean;
|
|
38
|
+
staged: number;
|
|
39
|
+
rejected: Array<{
|
|
40
|
+
control: string;
|
|
41
|
+
reason: string;
|
|
42
|
+
}>;
|
|
43
|
+
/** Supplied keys that are not proposable controls of this form. */
|
|
44
|
+
skipped: string[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Stage each supplied value as a proposal for local human review. Unknown,
|
|
48
|
+
* secret, read-only, or disabled controls are skipped — never written — and a
|
|
49
|
+
* proposal never applies or submits anything.
|
|
50
|
+
*/
|
|
51
|
+
export declare function stageControlProposals(registry: ControlInteractionRegistry, formId: string, values: Record<string, unknown>, context?: {
|
|
52
|
+
source?: ControlCommandSource;
|
|
53
|
+
actorId?: string;
|
|
54
|
+
}, options?: ControlProposalOptions): Promise<StageControlProposalsResult>;
|
|
55
|
+
//# sourceMappingURL=control-proposals.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"control-proposals.d.ts","sourceRoot":"","sources":["../../../src/components/forms/control-proposals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAGV,oBAAoB,EACpB,0BAA0B,EAC1B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,+DAA+D;AAC/D,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAM9D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAQtE;AAQD,oDAAoD;AACpD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,eAAe,GACxB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA8DzB;AAED,MAAM,WAAW,sBAAsB;IACrC,mEAAmE;IACnE,OAAO,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,0BAA0B,EACpC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,sBAA2B,GACnC,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,CAW9B;AAED,sEAAsE;AACtE,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,0BAA0B,EACpC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAUzC;AAED,uEAAuE;AACvE,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,0BAA0B,EACpC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAMzB;AAED,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,mEAAmE;IACnE,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,0BAA0B,EACpC,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,EACjE,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,2BAA2B,CAAC,CAwCtC"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Form-level proposals over a control interaction registry.
|
|
3
|
+
*
|
|
4
|
+
* Transport-neutral helpers for "propose values for this form" adapters (the
|
|
5
|
+
* rich smrt-svelte `<Form webmcp>` tool, `FormScope`, a chat or voice
|
|
6
|
+
* adapter). They read whatever the registry holds for one `formId` — every
|
|
7
|
+
* smrt-ui primitive (`Input`, `Select`, `Textarea`, `Combobox`, …) and every
|
|
8
|
+
* composite registered through `useControlRegistration` — and only ever STAGE
|
|
9
|
+
* values: a person applies them through `StagedControlReview`.
|
|
10
|
+
*/
|
|
11
|
+
/** Tool-safe name (`[a-z0-9_]`) for a form's proposal tool. */
|
|
12
|
+
export function controlProposalToolName(formId) {
|
|
13
|
+
const base = formId
|
|
14
|
+
.toLowerCase()
|
|
15
|
+
.replace(/[^a-z0-9_]+/g, '_')
|
|
16
|
+
.replace(/^_+|_+$/g, '');
|
|
17
|
+
return `${base || 'form'}_stage_changes`;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Whether a control can take a proposal at all: never secret or sensitive,
|
|
21
|
+
* never declared unwritable, disabled, or read-only, and never a file or
|
|
22
|
+
* password (neither is representable as a proposed JSON value).
|
|
23
|
+
*/
|
|
24
|
+
export function isControlProposable(snapshot) {
|
|
25
|
+
const { metadata, state } = snapshot;
|
|
26
|
+
if (metadata.sensitivity === 'secret' || metadata.sensitivity === 'sensitive')
|
|
27
|
+
return false;
|
|
28
|
+
if (metadata.writable === false)
|
|
29
|
+
return false;
|
|
30
|
+
if (metadata.kind === 'file' || metadata.kind === 'password')
|
|
31
|
+
return false;
|
|
32
|
+
if (state.disabled || state.readonly)
|
|
33
|
+
return false;
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
function numericConstraint(value) {
|
|
37
|
+
if (value === undefined || value === '')
|
|
38
|
+
return undefined;
|
|
39
|
+
const number = Number(value);
|
|
40
|
+
return Number.isFinite(number) ? number : undefined;
|
|
41
|
+
}
|
|
42
|
+
/** JSON Schema for one control's proposed value. */
|
|
43
|
+
export function controlProposalSchema(snapshot) {
|
|
44
|
+
const { metadata } = snapshot;
|
|
45
|
+
const constraints = metadata.constraints ?? {};
|
|
46
|
+
const optionValues = metadata.options?.map((option) => option.value);
|
|
47
|
+
let schema;
|
|
48
|
+
if (metadata.valueSchema) {
|
|
49
|
+
schema = { ...metadata.valueSchema };
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
switch (metadata.kind) {
|
|
53
|
+
case 'number':
|
|
54
|
+
case 'slider': {
|
|
55
|
+
schema = { type: 'number' };
|
|
56
|
+
const minimum = numericConstraint(constraints.min);
|
|
57
|
+
const maximum = numericConstraint(constraints.max);
|
|
58
|
+
if (minimum !== undefined)
|
|
59
|
+
schema.minimum = minimum;
|
|
60
|
+
if (maximum !== undefined)
|
|
61
|
+
schema.maximum = maximum;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
case 'checkbox':
|
|
65
|
+
case 'switch':
|
|
66
|
+
case 'toggle-button':
|
|
67
|
+
schema = { type: 'boolean' };
|
|
68
|
+
break;
|
|
69
|
+
case 'multi-select':
|
|
70
|
+
case 'tags-input':
|
|
71
|
+
schema = {
|
|
72
|
+
type: 'array',
|
|
73
|
+
items: optionValues?.length
|
|
74
|
+
? { enum: optionValues }
|
|
75
|
+
: { type: 'string' },
|
|
76
|
+
};
|
|
77
|
+
break;
|
|
78
|
+
case 'range-slider':
|
|
79
|
+
schema = {
|
|
80
|
+
type: 'array',
|
|
81
|
+
items: { type: 'number' },
|
|
82
|
+
minItems: 2,
|
|
83
|
+
maxItems: 2,
|
|
84
|
+
};
|
|
85
|
+
break;
|
|
86
|
+
case 'custom':
|
|
87
|
+
// A composite without a declared valueSchema: describe, don't guess.
|
|
88
|
+
schema = {};
|
|
89
|
+
break;
|
|
90
|
+
default:
|
|
91
|
+
schema = { type: 'string' };
|
|
92
|
+
if (typeof constraints.minLength === 'number' &&
|
|
93
|
+
constraints.minLength > 0)
|
|
94
|
+
schema.minLength = constraints.minLength;
|
|
95
|
+
if (typeof constraints.maxLength === 'number' &&
|
|
96
|
+
constraints.maxLength > 0)
|
|
97
|
+
schema.maxLength = constraints.maxLength;
|
|
98
|
+
if (optionValues?.length)
|
|
99
|
+
schema.enum = optionValues;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (metadata.label)
|
|
103
|
+
schema.title = metadata.label;
|
|
104
|
+
if (metadata.description)
|
|
105
|
+
schema.description = metadata.description;
|
|
106
|
+
return schema;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* The proposable controls of one form, keyed by control id. A
|
|
110
|
+
* record-qualified control (subject) shares its controlId with its siblings;
|
|
111
|
+
* the first registered wins so the schema key stays unambiguous.
|
|
112
|
+
*/
|
|
113
|
+
export function proposableControls(registry, formId, options = {}) {
|
|
114
|
+
const excluded = new Set(options.exclude ?? []);
|
|
115
|
+
const controls = new Map();
|
|
116
|
+
for (const snapshot of registry.list(formId)) {
|
|
117
|
+
const { controlId } = snapshot.identity;
|
|
118
|
+
if (snapshot.identity.formId !== formId || excluded.has(controlId))
|
|
119
|
+
continue;
|
|
120
|
+
if (!isControlProposable(snapshot) || controls.has(controlId))
|
|
121
|
+
continue;
|
|
122
|
+
controls.set(controlId, snapshot);
|
|
123
|
+
}
|
|
124
|
+
return controls;
|
|
125
|
+
}
|
|
126
|
+
/** `{ controlId: schema }` for every proposable control of a form. */
|
|
127
|
+
export function controlProposalProperties(registry, formId, options = {}) {
|
|
128
|
+
const properties = {};
|
|
129
|
+
for (const [controlId, snapshot] of proposableControls(registry, formId, options)) {
|
|
130
|
+
properties[controlId] = controlProposalSchema(snapshot);
|
|
131
|
+
}
|
|
132
|
+
return properties;
|
|
133
|
+
}
|
|
134
|
+
/** Object schema with one optional property per proposable control. */
|
|
135
|
+
export function controlProposalInputSchema(registry, formId, options = {}) {
|
|
136
|
+
return {
|
|
137
|
+
type: 'object',
|
|
138
|
+
additionalProperties: false,
|
|
139
|
+
properties: controlProposalProperties(registry, formId, options),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Stage each supplied value as a proposal for local human review. Unknown,
|
|
144
|
+
* secret, read-only, or disabled controls are skipped — never written — and a
|
|
145
|
+
* proposal never applies or submits anything.
|
|
146
|
+
*/
|
|
147
|
+
export async function stageControlProposals(registry, formId, values, context = {}, options = {}) {
|
|
148
|
+
const controls = proposableControls(registry, formId, options);
|
|
149
|
+
const commands = [];
|
|
150
|
+
const skipped = [];
|
|
151
|
+
for (const [controlId, value] of Object.entries(values ?? {})) {
|
|
152
|
+
const snapshot = controls.get(controlId);
|
|
153
|
+
if (!snapshot) {
|
|
154
|
+
skipped.push(controlId);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
commands.push({ action: 'stage', identity: snapshot.identity, value });
|
|
158
|
+
}
|
|
159
|
+
if (commands.length === 0) {
|
|
160
|
+
return { ok: false, staged: 0, rejected: [], skipped };
|
|
161
|
+
}
|
|
162
|
+
const commandContext = {
|
|
163
|
+
source: context.source ?? 'agent',
|
|
164
|
+
...(context.actorId ? { actorId: context.actorId } : {}),
|
|
165
|
+
};
|
|
166
|
+
let results;
|
|
167
|
+
if (registry.executeBatch) {
|
|
168
|
+
results = (await registry.executeBatch(commands, commandContext)).results;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
results = [];
|
|
172
|
+
for (const command of commands) {
|
|
173
|
+
results.push(await registry.execute(command, commandContext));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const rejected = results
|
|
177
|
+
.filter((result) => !result.ok)
|
|
178
|
+
.map((result) => ({
|
|
179
|
+
control: result.identity.controlId,
|
|
180
|
+
reason: result.reason ?? 'rejected',
|
|
181
|
+
}));
|
|
182
|
+
return {
|
|
183
|
+
ok: rejected.length === 0,
|
|
184
|
+
staged: results.length - rejected.length,
|
|
185
|
+
rejected,
|
|
186
|
+
skipped,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface DictationTranscribeOptions {
|
|
2
|
+
/** The recording's MIME type, e.g. `audio/webm;codecs=opus`. */
|
|
3
|
+
mimeType: string;
|
|
4
|
+
/** BCP-47 language the person speaks, e.g. `en-US`. */
|
|
5
|
+
language: string;
|
|
6
|
+
/** How long the recording is, in milliseconds. */
|
|
7
|
+
durationMs: number;
|
|
8
|
+
/** Aborted when the person cancels or the form goes away. */
|
|
9
|
+
signal: AbortSignal;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Writes down a recorded message. Resolve the text (empty when nothing was
|
|
13
|
+
* said); reject to show an error. Throw a `DictationError` to pick the
|
|
14
|
+
* message (`too-long`, `not-transcribed`, `unavailable`, …).
|
|
15
|
+
*/
|
|
16
|
+
export type DictationTranscribe = (audio: Blob, options: DictationTranscribeOptions) => Promise<string | {
|
|
17
|
+
text: string;
|
|
18
|
+
}>;
|
|
19
|
+
export interface HttpTranscriberOptions {
|
|
20
|
+
/** `fetch` to use (default `globalThis.fetch`). */
|
|
21
|
+
fetch?: typeof fetch;
|
|
22
|
+
/** Extra request headers. */
|
|
23
|
+
headers?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* A `DictationTranscribe` that POSTs the audio to `url` as the raw request
|
|
27
|
+
* body (`Content-Type` is the recording's type) with `language` and
|
|
28
|
+
* `durationMs` in the query string, and reads `{ text }` back.
|
|
29
|
+
*
|
|
30
|
+
* Status codes become plain-words errors: 413 → `too-long`, 503 →
|
|
31
|
+
* `unavailable` (not set up), 401/403 → `forbidden`, anything else →
|
|
32
|
+
* `not-transcribed`. The server's `{ error }` text is kept as the error
|
|
33
|
+
* message for logs, not shown.
|
|
34
|
+
*/
|
|
35
|
+
export declare function createHttpTranscriber(url: string | URL, options?: HttpTranscriberOptions): DictationTranscribe;
|
|
36
|
+
//# sourceMappingURL=dictation-transcribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dictation-transcribe.d.ts","sourceRoot":"","sources":["../../../src/components/forms/dictation-transcribe.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,0BAA0B;IACzC,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,MAAM,EAAE,WAAW,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAChC,KAAK,EAAE,IAAI,EACX,OAAO,EAAE,0BAA0B,KAChC,OAAO,CAAC,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAExC,MAAM,WAAW,sBAAsB;IACrC,mDAAmD;IACnD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,MAAM,GAAG,GAAG,EACjB,OAAO,GAAE,sBAA2B,GACnC,mBAAmB,CAoDrB"}
|