@orz/ui 0.0.0-stage → 0.1.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/LICENSE +29 -0
- package/README.md +541 -2
- package/dist/components/avatar.d.ts +14 -0
- package/dist/components/avatar.d.ts.map +1 -0
- package/dist/components/avatar.js +23 -0
- package/dist/components/avatar.js.map +1 -0
- package/dist/components/badge.d.ts +10 -0
- package/dist/components/badge.d.ts.map +1 -0
- package/dist/components/badge.js +26 -0
- package/dist/components/badge.js.map +1 -0
- package/dist/components/button.d.ts +11 -0
- package/dist/components/button.d.ts.map +1 -0
- package/dist/components/button.js +40 -0
- package/dist/components/button.js.map +1 -0
- package/dist/components/card.d.ts +12 -0
- package/dist/components/card.d.ts.map +1 -0
- package/dist/components/card.js +25 -0
- package/dist/components/card.js.map +1 -0
- package/dist/components/color-picker.d.ts +58 -0
- package/dist/components/color-picker.d.ts.map +1 -0
- package/dist/components/color-picker.js +950 -0
- package/dist/components/color-picker.js.map +1 -0
- package/dist/components/confirm-dialog.d.ts +26 -0
- package/dist/components/confirm-dialog.d.ts.map +1 -0
- package/dist/components/confirm-dialog.js +18 -0
- package/dist/components/confirm-dialog.js.map +1 -0
- package/dist/components/dialog.d.ts +26 -0
- package/dist/components/dialog.d.ts.map +1 -0
- package/dist/components/dialog.js +149 -0
- package/dist/components/dialog.js.map +1 -0
- package/dist/components/dropdown-menu.d.ts +30 -0
- package/dist/components/dropdown-menu.d.ts.map +1 -0
- package/dist/components/dropdown-menu.js +100 -0
- package/dist/components/dropdown-menu.js.map +1 -0
- package/dist/components/filter-menu.d.ts +98 -0
- package/dist/components/filter-menu.d.ts.map +1 -0
- package/dist/components/filter-menu.js +745 -0
- package/dist/components/filter-menu.js.map +1 -0
- package/dist/components/highlighted-input.d.ts +40 -0
- package/dist/components/highlighted-input.d.ts.map +1 -0
- package/dist/components/highlighted-input.js +95 -0
- package/dist/components/highlighted-input.js.map +1 -0
- package/dist/components/hover-card.d.ts +7 -0
- package/dist/components/hover-card.d.ts.map +1 -0
- package/dist/components/hover-card.js +22 -0
- package/dist/components/hover-card.js.map +1 -0
- package/dist/components/input.d.ts +4 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +7 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/label.d.ts +5 -0
- package/dist/components/label.d.ts.map +1 -0
- package/dist/components/label.js +8 -0
- package/dist/components/label.js.map +1 -0
- package/dist/components/link-chip.d.ts +67 -0
- package/dist/components/link-chip.d.ts.map +1 -0
- package/dist/components/link-chip.js +71 -0
- package/dist/components/link-chip.js.map +1 -0
- package/dist/components/load-failure.d.ts +24 -0
- package/dist/components/load-failure.d.ts.map +1 -0
- package/dist/components/load-failure.js +27 -0
- package/dist/components/load-failure.js.map +1 -0
- package/dist/components/load-more.d.ts +15 -0
- package/dist/components/load-more.d.ts.map +1 -0
- package/dist/components/load-more.js +25 -0
- package/dist/components/load-more.js.map +1 -0
- package/dist/components/media-controls.d.ts +50 -0
- package/dist/components/media-controls.d.ts.map +1 -0
- package/dist/components/media-controls.js +71 -0
- package/dist/components/media-controls.js.map +1 -0
- package/dist/components/popover.d.ts +9 -0
- package/dist/components/popover.d.ts.map +1 -0
- package/dist/components/popover.js +82 -0
- package/dist/components/popover.js.map +1 -0
- package/dist/components/query-box.d.ts +78 -0
- package/dist/components/query-box.d.ts.map +1 -0
- package/dist/components/query-box.js +332 -0
- package/dist/components/query-box.js.map +1 -0
- package/dist/components/query-highlight.d.ts +14 -0
- package/dist/components/query-highlight.d.ts.map +1 -0
- package/dist/components/query-highlight.js +132 -0
- package/dist/components/query-highlight.js.map +1 -0
- package/dist/components/select.d.ts +19 -0
- package/dist/components/select.d.ts.map +1 -0
- package/dist/components/select.js +38 -0
- package/dist/components/select.js.map +1 -0
- package/dist/components/separator.d.ts +5 -0
- package/dist/components/separator.d.ts.map +1 -0
- package/dist/components/separator.js +9 -0
- package/dist/components/separator.js.map +1 -0
- package/dist/components/sheet.d.ts +16 -0
- package/dist/components/sheet.d.ts.map +1 -0
- package/dist/components/sheet.js +41 -0
- package/dist/components/sheet.js.map +1 -0
- package/dist/components/skeleton.d.ts +3 -0
- package/dist/components/skeleton.d.ts.map +1 -0
- package/dist/components/skeleton.js +7 -0
- package/dist/components/skeleton.js.map +1 -0
- package/dist/components/slider.d.ts +8 -0
- package/dist/components/slider.d.ts.map +1 -0
- package/dist/components/slider.js +14 -0
- package/dist/components/slider.js.map +1 -0
- package/dist/components/sonner.d.ts +4 -0
- package/dist/components/sonner.d.ts.map +1 -0
- package/dist/components/sonner.js +26 -0
- package/dist/components/sonner.js.map +1 -0
- package/dist/components/switch.d.ts +5 -0
- package/dist/components/switch.d.ts.map +1 -0
- package/dist/components/switch.js +8 -0
- package/dist/components/switch.js.map +1 -0
- package/dist/components/table.d.ts +11 -0
- package/dist/components/table.d.ts.map +1 -0
- package/dist/components/table.js +28 -0
- package/dist/components/table.js.map +1 -0
- package/dist/components/tabs.d.ts +8 -0
- package/dist/components/tabs.d.ts.map +1 -0
- package/dist/components/tabs.js +18 -0
- package/dist/components/tabs.js.map +1 -0
- package/dist/components/textarea.d.ts +4 -0
- package/dist/components/textarea.d.ts.map +1 -0
- package/dist/components/textarea.js +7 -0
- package/dist/components/textarea.js.map +1 -0
- package/dist/components/theme-editor/draft.d.ts +235 -0
- package/dist/components/theme-editor/draft.d.ts.map +1 -0
- package/dist/components/theme-editor/draft.js +638 -0
- package/dist/components/theme-editor/draft.js.map +1 -0
- package/dist/components/theme-editor/export-panel.d.ts +14 -0
- package/dist/components/theme-editor/export-panel.d.ts.map +1 -0
- package/dist/components/theme-editor/export-panel.js +39 -0
- package/dist/components/theme-editor/export-panel.js.map +1 -0
- package/dist/components/theme-editor/form.d.ts +69 -0
- package/dist/components/theme-editor/form.d.ts.map +1 -0
- package/dist/components/theme-editor/form.js +350 -0
- package/dist/components/theme-editor/form.js.map +1 -0
- package/dist/components/theme-editor/import-panel.d.ts +21 -0
- package/dist/components/theme-editor/import-panel.d.ts.map +1 -0
- package/dist/components/theme-editor/import-panel.js +117 -0
- package/dist/components/theme-editor/import-panel.js.map +1 -0
- package/dist/components/theme-editor/theme-list.d.ts +23 -0
- package/dist/components/theme-editor/theme-list.d.ts.map +1 -0
- package/dist/components/theme-editor/theme-list.js +42 -0
- package/dist/components/theme-editor/theme-list.js.map +1 -0
- package/dist/components/theme-editor/token-list.d.ts +30 -0
- package/dist/components/theme-editor/token-list.d.ts.map +1 -0
- package/dist/components/theme-editor/token-list.js +124 -0
- package/dist/components/theme-editor/token-list.js.map +1 -0
- package/dist/components/theme-editor/tokens.d.ts +37 -0
- package/dist/components/theme-editor/tokens.d.ts.map +1 -0
- package/dist/components/theme-editor/tokens.js +155 -0
- package/dist/components/theme-editor/tokens.js.map +1 -0
- package/dist/components/theme-editor.d.ts +26 -0
- package/dist/components/theme-editor.d.ts.map +1 -0
- package/dist/components/theme-editor.js +467 -0
- package/dist/components/theme-editor.js.map +1 -0
- package/dist/components/theme-menu.d.ts +28 -0
- package/dist/components/theme-menu.d.ts.map +1 -0
- package/dist/components/theme-menu.js +308 -0
- package/dist/components/theme-menu.js.map +1 -0
- package/dist/components/tooltip.d.ts +11 -0
- package/dist/components/tooltip.d.ts.map +1 -0
- package/dist/components/tooltip.js +18 -0
- package/dist/components/tooltip.js.map +1 -0
- package/dist/components/unsaved-changes-dialog.d.ts +39 -0
- package/dist/components/unsaved-changes-dialog.d.ts.map +1 -0
- package/dist/components/unsaved-changes-dialog.js +46 -0
- package/dist/components/unsaved-changes-dialog.js.map +1 -0
- package/dist/hooks/scroll-insets.d.ts +89 -0
- package/dist/hooks/scroll-insets.d.ts.map +1 -0
- package/dist/hooks/scroll-insets.js +265 -0
- package/dist/hooks/scroll-insets.js.map +1 -0
- package/dist/hooks/use-element-height.d.ts +11 -0
- package/dist/hooks/use-element-height.d.ts.map +1 -0
- package/dist/hooks/use-element-height.js +33 -0
- package/dist/hooks/use-element-height.js.map +1 -0
- package/dist/hooks/use-media-query.d.ts +22 -0
- package/dist/hooks/use-media-query.d.ts.map +1 -0
- package/dist/hooks/use-media-query.js +38 -0
- package/dist/hooks/use-media-query.js.map +1 -0
- package/dist/hooks/use-overlay-scrollbars.d.ts +31 -0
- package/dist/hooks/use-overlay-scrollbars.d.ts.map +1 -0
- package/dist/hooks/use-overlay-scrollbars.js +80 -0
- package/dist/hooks/use-overlay-scrollbars.js.map +1 -0
- package/dist/hooks/use-paged-append.d.ts +7 -0
- package/dist/hooks/use-paged-append.d.ts.map +1 -0
- package/dist/hooks/use-paged-append.js +53 -0
- package/dist/hooks/use-paged-append.js.map +1 -0
- package/dist/hooks/use-query-completion.d.ts +27 -0
- package/dist/hooks/use-query-completion.d.ts.map +1 -0
- package/dist/hooks/use-query-completion.js +100 -0
- package/dist/hooks/use-query-completion.js.map +1 -0
- package/dist/hooks/use-slash-shortcut.d.ts +9 -0
- package/dist/hooks/use-slash-shortcut.d.ts.map +1 -0
- package/dist/hooks/use-slash-shortcut.js +38 -0
- package/dist/hooks/use-slash-shortcut.js.map +1 -0
- package/dist/hooks/viewport-chrome.d.ts +50 -0
- package/dist/hooks/viewport-chrome.d.ts.map +1 -0
- package/dist/hooks/viewport-chrome.js +170 -0
- package/dist/hooks/viewport-chrome.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/cell-width-table.d.ts +3 -0
- package/dist/lib/cell-width-table.d.ts.map +1 -0
- package/dist/lib/cell-width-table.js +6 -0
- package/dist/lib/cell-width-table.js.map +1 -0
- package/dist/lib/cell-width.d.ts +7 -0
- package/dist/lib/cell-width.d.ts.map +1 -0
- package/dist/lib/cell-width.js +50 -0
- package/dist/lib/cell-width.js.map +1 -0
- package/dist/lib/clipboard.d.ts +8 -0
- package/dist/lib/clipboard.d.ts.map +1 -0
- package/dist/lib/clipboard.js +19 -0
- package/dist/lib/clipboard.js.map +1 -0
- package/dist/lib/color.d.ts +40 -0
- package/dist/lib/color.d.ts.map +1 -0
- package/dist/lib/color.js +177 -0
- package/dist/lib/color.js.map +1 -0
- package/dist/lib/scroll-map.d.ts +20 -0
- package/dist/lib/scroll-map.d.ts.map +1 -0
- package/dist/lib/scroll-map.js +46 -0
- package/dist/lib/scroll-map.js.map +1 -0
- package/dist/lib/text-metrics.d.ts +33 -0
- package/dist/lib/text-metrics.d.ts.map +1 -0
- package/dist/lib/text-metrics.js +89 -0
- package/dist/lib/text-metrics.js.map +1 -0
- package/dist/lib/theme-file.d.ts +51 -0
- package/dist/lib/theme-file.d.ts.map +1 -0
- package/dist/lib/theme-file.js +511 -0
- package/dist/lib/theme-file.js.map +1 -0
- package/dist/lib/unsaved-guard.d.ts +33 -0
- package/dist/lib/unsaved-guard.d.ts.map +1 -0
- package/dist/lib/unsaved-guard.js +37 -0
- package/dist/lib/unsaved-guard.js.map +1 -0
- package/dist/lib/utils.d.ts +3 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/utils.js +6 -0
- package/dist/lib/utils.js.map +1 -0
- package/dist/theme/custom.d.ts +8 -0
- package/dist/theme/custom.d.ts.map +1 -0
- package/dist/theme/custom.js +56 -0
- package/dist/theme/custom.js.map +1 -0
- package/dist/theme/index.d.ts +6 -0
- package/dist/theme/index.d.ts.map +1 -0
- package/dist/theme/index.js +5 -0
- package/dist/theme/index.js.map +1 -0
- package/dist/theme/paint-guard.d.ts +20 -0
- package/dist/theme/paint-guard.d.ts.map +1 -0
- package/dist/theme/paint-guard.js +120 -0
- package/dist/theme/paint-guard.js.map +1 -0
- package/dist/theme/picks.d.ts +22 -0
- package/dist/theme/picks.d.ts.map +1 -0
- package/dist/theme/picks.js +43 -0
- package/dist/theme/picks.js.map +1 -0
- package/dist/theme/pref.d.ts +39 -0
- package/dist/theme/pref.d.ts.map +1 -0
- package/dist/theme/pref.js +129 -0
- package/dist/theme/pref.js.map +1 -0
- package/dist/theme/provider.d.ts +41 -0
- package/dist/theme/provider.d.ts.map +1 -0
- package/dist/theme/provider.js +103 -0
- package/dist/theme/provider.js.map +1 -0
- package/dist/theme/store.d.ts +104 -0
- package/dist/theme/store.d.ts.map +1 -0
- package/dist/theme/store.js +509 -0
- package/dist/theme/store.js.map +1 -0
- package/package.json +94 -3
- package/styles.css +79 -0
- package/theme.css +186 -0
- package/vendor/shadcn-tailwind.css +664 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useRef, useState } from "react";
|
|
2
|
+
function emptyState(key, generation) {
|
|
3
|
+
return { key, generation, pages: [], pending: false, error: null };
|
|
4
|
+
}
|
|
5
|
+
export function usePagedAppend(resetKey) {
|
|
6
|
+
const [state, setState] = useState(() => emptyState(resetKey, 0));
|
|
7
|
+
const inFlight = useRef(null);
|
|
8
|
+
const nextGeneration = state.generation + 1;
|
|
9
|
+
if (state.key !== resetKey) {
|
|
10
|
+
setState(emptyState(resetKey, nextGeneration));
|
|
11
|
+
}
|
|
12
|
+
const current = state.key === resetKey ? state : emptyState(resetKey, nextGeneration);
|
|
13
|
+
function append(load) {
|
|
14
|
+
const requestKey = resetKey;
|
|
15
|
+
const requestGeneration = current.generation;
|
|
16
|
+
if (inFlight.current === requestGeneration)
|
|
17
|
+
return;
|
|
18
|
+
inFlight.current = requestGeneration;
|
|
19
|
+
setState((value) => value.key === requestKey && value.generation === requestGeneration
|
|
20
|
+
? { ...value, pending: true }
|
|
21
|
+
: value);
|
|
22
|
+
void (async () => {
|
|
23
|
+
try {
|
|
24
|
+
const page = await load();
|
|
25
|
+
setState((value) => value.key === requestKey && value.generation === requestGeneration
|
|
26
|
+
? {
|
|
27
|
+
...value,
|
|
28
|
+
pages: [...value.pages, page],
|
|
29
|
+
pending: false,
|
|
30
|
+
error: null,
|
|
31
|
+
}
|
|
32
|
+
: value);
|
|
33
|
+
}
|
|
34
|
+
catch (caught) {
|
|
35
|
+
const error = caught instanceof Error ? caught : new Error(String(caught));
|
|
36
|
+
setState((value) => value.key === requestKey && value.generation === requestGeneration
|
|
37
|
+
? { ...value, pending: false, error }
|
|
38
|
+
: value);
|
|
39
|
+
}
|
|
40
|
+
finally {
|
|
41
|
+
if (inFlight.current === requestGeneration)
|
|
42
|
+
inFlight.current = null;
|
|
43
|
+
}
|
|
44
|
+
})();
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
pages: current.pages,
|
|
48
|
+
pending: current.pending,
|
|
49
|
+
error: current.error,
|
|
50
|
+
append,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=use-paged-append.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-paged-append.js","sourceRoot":"","sources":["../../src/hooks/use-paged-append.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type CompiledQuerySchema, type CompletionRow, type QueryPart, type SourceResult, type ValueProviders } from "@orz/query";
|
|
2
|
+
/**
|
|
3
|
+
* The completions for the caret in a query: keys while a word is typed,
|
|
4
|
+
* values after a known key's colon.
|
|
5
|
+
*
|
|
6
|
+
* Snapshot providers answer at once. A `search` provider is asked for the
|
|
7
|
+
* typed prefix after 150 ms of quiet; a newer prefix aborts the request in
|
|
8
|
+
* flight, and answers are kept per source and prefix for the life of the
|
|
9
|
+
* component, so backspacing over a prefix asks nothing again. A rejected
|
|
10
|
+
* search leaves only the values the schema itself knows: the box has to keep
|
|
11
|
+
* working without completions, and nothing about it is the reader's to fix.
|
|
12
|
+
*
|
|
13
|
+
* With `enabled: false` nothing is offered and nothing is fetched; `parts`
|
|
14
|
+
* are still parsed, since the highlighting needs them either way.
|
|
15
|
+
*/
|
|
16
|
+
export declare function useQueryCompletion({ schema, value, caret, providers, enabled, }: {
|
|
17
|
+
schema: CompiledQuerySchema;
|
|
18
|
+
value: string;
|
|
19
|
+
caret: number;
|
|
20
|
+
providers: ValueProviders;
|
|
21
|
+
enabled?: boolean;
|
|
22
|
+
}): {
|
|
23
|
+
parts: QueryPart[];
|
|
24
|
+
completions: SourceResult<CompletionRow>[];
|
|
25
|
+
loading: boolean;
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=use-query-completion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-query-completion.d.ts","sourceRoot":"","sources":["../../src/hooks/use-query-completion.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAKlB,KAAK,SAAS,EAEd,KAAK,YAAY,EAIjB,KAAK,cAAc,EAEpB,MAAM,YAAY,CAAC;AASpB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,KAAK,EACL,KAAK,EACL,SAAS,EACT,OAAc,GACf,EAAE;IACD,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,cAAc,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG;IACF,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,WAAW,EAAE,YAAY,CAAC,aAAa,CAAC,EAAE,CAAC;IAC3C,OAAO,EAAE,OAAO,CAAC;CAClB,CA4FA"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { filterOptions, keyRows, parseQuery, providerOf, queryCompletionSlot, slotValues, valueRows, } from "@orz/query";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "react";
|
|
3
|
+
/** Quiet time before a `search` provider is asked, so typing is not a request per key. */
|
|
4
|
+
const FETCH_DELAY = 150;
|
|
5
|
+
/**
|
|
6
|
+
* The completions for the caret in a query: keys while a word is typed,
|
|
7
|
+
* values after a known key's colon.
|
|
8
|
+
*
|
|
9
|
+
* Snapshot providers answer at once. A `search` provider is asked for the
|
|
10
|
+
* typed prefix after 150 ms of quiet; a newer prefix aborts the request in
|
|
11
|
+
* flight, and answers are kept per source and prefix for the life of the
|
|
12
|
+
* component, so backspacing over a prefix asks nothing again. A rejected
|
|
13
|
+
* search leaves only the values the schema itself knows: the box has to keep
|
|
14
|
+
* working without completions, and nothing about it is the reader's to fix.
|
|
15
|
+
*
|
|
16
|
+
* With `enabled: false` nothing is offered and nothing is fetched; `parts`
|
|
17
|
+
* are still parsed, since the highlighting needs them either way.
|
|
18
|
+
*/
|
|
19
|
+
export function useQueryCompletion({ schema, value, caret, providers, enabled = true, }) {
|
|
20
|
+
const parts = useMemo(() => parseQuery(schema, value), [schema, value]);
|
|
21
|
+
const slot = useMemo(() => (enabled ? queryCompletionSlot(schema, parts, value, caret) : null), [enabled, schema, parts, value, caret]);
|
|
22
|
+
const cache = useRef(new Map());
|
|
23
|
+
const [failed, setFailed] = useState(null);
|
|
24
|
+
const [, setAnswered] = useState(0);
|
|
25
|
+
let provider;
|
|
26
|
+
let wanted = null;
|
|
27
|
+
if (slot?.kind === "value") {
|
|
28
|
+
provider = providerOf(slot.spec, providers);
|
|
29
|
+
const source = slot.spec.kind === "enum" ? undefined : slot.spec.source;
|
|
30
|
+
if (provider !== undefined &&
|
|
31
|
+
"search" in provider &&
|
|
32
|
+
source !== undefined) {
|
|
33
|
+
wanted = {
|
|
34
|
+
cacheKey: `${source}\u0000${slot.typed}`,
|
|
35
|
+
source,
|
|
36
|
+
typed: slot.typed,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// The effect reads the provider through a ref: an app that rebuilds its
|
|
41
|
+
// providers object on every render would otherwise restart the quiet
|
|
42
|
+
// period on every render and never get to ask.
|
|
43
|
+
const latest = useRef(providers);
|
|
44
|
+
latest.current = providers;
|
|
45
|
+
const cacheKey = wanted?.cacheKey;
|
|
46
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: `cacheKey` stands for `wanted`, which is rebuilt every render
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (wanted === null || cache.current.has(wanted.cacheKey))
|
|
49
|
+
return;
|
|
50
|
+
const { cacheKey: key, source, typed } = wanted;
|
|
51
|
+
// Asking again, so the old failure no longer speaks for this prefix.
|
|
52
|
+
setFailed((f) => (f === key ? null : f));
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
const timer = setTimeout(() => {
|
|
55
|
+
const current = latest.current[source];
|
|
56
|
+
if (current === undefined || !("search" in current))
|
|
57
|
+
return;
|
|
58
|
+
current.search(typed, controller.signal).then((answer) => {
|
|
59
|
+
if (controller.signal.aborted)
|
|
60
|
+
return;
|
|
61
|
+
cache.current.set(key, answer);
|
|
62
|
+
setAnswered((n) => n + 1);
|
|
63
|
+
}, () => {
|
|
64
|
+
if (controller.signal.aborted)
|
|
65
|
+
return;
|
|
66
|
+
setFailed(key);
|
|
67
|
+
});
|
|
68
|
+
}, FETCH_DELAY);
|
|
69
|
+
return () => {
|
|
70
|
+
clearTimeout(timer);
|
|
71
|
+
controller.abort();
|
|
72
|
+
};
|
|
73
|
+
}, [cacheKey]);
|
|
74
|
+
const completions = [];
|
|
75
|
+
let loading = false;
|
|
76
|
+
if (slot !== null) {
|
|
77
|
+
completions.push(keyRows(schema, value, slot));
|
|
78
|
+
if (slot.kind === "value") {
|
|
79
|
+
const options = slotValues(slot, provider);
|
|
80
|
+
if (options !== "fetch") {
|
|
81
|
+
completions.push(valueRows(schema, value, slot, filterOptions(options, slot.typed)));
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// The schema's own values (an enum's set, the specials) need no
|
|
85
|
+
// request, so they are offered first and at once; the provider's
|
|
86
|
+
// answer follows as it was given, already matched to the prefix.
|
|
87
|
+
const builtins = slotValues(slot);
|
|
88
|
+
const known = builtins === "fetch" ? [] : builtins;
|
|
89
|
+
const fetched = wanted === null ? undefined : cache.current.get(wanted.cacheKey);
|
|
90
|
+
loading = fetched === undefined && failed !== wanted?.cacheKey;
|
|
91
|
+
completions.push(valueRows(schema, value, slot, [
|
|
92
|
+
...filterOptions(known, slot.typed),
|
|
93
|
+
...(fetched ?? []),
|
|
94
|
+
]));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { parts, completions, loading };
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=use-query-completion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-query-completion.js","sourceRoot":"","sources":["../../src/hooks/use-query-completion.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,aAAa,EACb,OAAO,EACP,UAAU,EACV,UAAU,EAEV,mBAAmB,EAEnB,UAAU,EAIV,SAAS,GACV,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE7D,0FAA0F;AAC1F,MAAM,WAAW,GAAG,GAAG,CAAC;AAKxB;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,MAAM,EACN,KAAK,EACL,KAAK,EACL,SAAS,EACT,OAAO,GAAG,IAAI,GAOf;IAKC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;IACxE,MAAM,IAAI,GAAG,OAAO,CAClB,GAAG,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,mBAAmB,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EACzE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,CACvC,CAAC;IAEF,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,GAAG,EAAkC,CAAC,CAAC;IAChE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAC1D,MAAM,CAAC,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAEpC,IAAI,QAAmC,CAAC;IACxC,IAAI,MAAM,GAAW,IAAI,CAAC;IAC1B,IAAI,IAAI,EAAE,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;QACxE,IACE,QAAQ,KAAK,SAAS;YACtB,QAAQ,IAAI,QAAQ;YACpB,MAAM,KAAK,SAAS,EACpB,CAAC;YACD,MAAM,GAAG;gBACP,QAAQ,EAAE,GAAG,MAAM,SAAS,IAAI,CAAC,KAAK,EAAE;gBACxC,MAAM;gBACN,KAAK,EAAE,IAAI,CAAC,KAAK;aAClB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,qEAAqE;IACrE,+CAA+C;IAC/C,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACjC,MAAM,CAAC,OAAO,GAAG,SAAS,CAAC;IAC3B,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,CAAC;IAClC,yHAAyH;IACzH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,OAAO;QAClE,MAAM,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,CAAC;QAChD,qEAAqE;QACrE,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YACvC,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,IAAI,OAAO,CAAC;gBAAE,OAAO;YAC5D,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,IAAI,CAC3C,CAAC,MAAM,EAAE,EAAE;gBACT,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAO;gBACtC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBAC/B,WAAW,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC5B,CAAC,EACD,GAAG,EAAE;gBACH,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO;oBAAE,OAAO;gBACtC,SAAS,CAAC,GAAG,CAAC,CAAC;YACjB,CAAC,CACF,CAAC;QACJ,CAAC,EAAE,WAAW,CAAC,CAAC;QAChB,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,MAAM,WAAW,GAAkC,EAAE,CAAC;IACtD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/C,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC3C,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;gBACxB,WAAW,CAAC,IAAI,CACd,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CACnE,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,gEAAgE;gBAChE,iEAAiE;gBACjE,iEAAiE;gBACjE,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;gBAClC,MAAM,KAAK,GAAG,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;gBACnD,MAAM,OAAO,GACX,MAAM,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACnE,OAAO,GAAG,OAAO,KAAK,SAAS,IAAI,MAAM,KAAK,MAAM,EAAE,QAAQ,CAAC;gBAC/D,WAAW,CAAC,IAAI,CACd,SAAS,CAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE;oBAC7B,GAAG,aAAa,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC;oBACnC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;iBACnB,CAAC,CACH,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AACzC,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/` reaches a search from anywhere on the page.
|
|
3
|
+
*
|
|
4
|
+
* Callers answer the key differently — a box takes focus, a collapsed toggle
|
|
5
|
+
* has to appear first — but the rule for when the key is ours to take is the
|
|
6
|
+
* same for all of them, and this is the one place it is written.
|
|
7
|
+
*/
|
|
8
|
+
export declare function useSlashShortcut(onSlash: () => void): void;
|
|
9
|
+
//# sourceMappingURL=use-slash-shortcut.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-slash-shortcut.d.ts","sourceRoot":"","sources":["../../src/hooks/use-slash-shortcut.ts"],"names":[],"mappings":"AAUA;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI,CAkB1D"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
/** Where `/` must not steal the keystroke: the reader is already typing. */
|
|
3
|
+
function isTypingTarget(target) {
|
|
4
|
+
const el = target;
|
|
5
|
+
if (!el)
|
|
6
|
+
return false;
|
|
7
|
+
if (el.isContentEditable)
|
|
8
|
+
return true;
|
|
9
|
+
return ["INPUT", "TEXTAREA", "SELECT"].includes(el.tagName);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* `/` reaches a search from anywhere on the page.
|
|
13
|
+
*
|
|
14
|
+
* Callers answer the key differently — a box takes focus, a collapsed toggle
|
|
15
|
+
* has to appear first — but the rule for when the key is ours to take is the
|
|
16
|
+
* same for all of them, and this is the one place it is written.
|
|
17
|
+
*/
|
|
18
|
+
export function useSlashShortcut(onSlash) {
|
|
19
|
+
// A search box re-renders on every keystroke; the listener must not be
|
|
20
|
+
// torn down and re-attached that often, so the callback goes in a ref.
|
|
21
|
+
const slash = useRef(onSlash);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
slash.current = onSlash;
|
|
24
|
+
});
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const onKey = (e) => {
|
|
27
|
+
if (e.key !== "/" || e.metaKey || e.ctrlKey || e.altKey)
|
|
28
|
+
return;
|
|
29
|
+
if (isTypingTarget(e.target))
|
|
30
|
+
return;
|
|
31
|
+
e.preventDefault();
|
|
32
|
+
slash.current();
|
|
33
|
+
};
|
|
34
|
+
window.addEventListener("keydown", onKey);
|
|
35
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
36
|
+
}, []);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=use-slash-shortcut.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-slash-shortcut.js","sourceRoot":"","sources":["../../src/hooks/use-slash-shortcut.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE1C,4EAA4E;AAC5E,SAAS,cAAc,CAAC,MAA0B;IAChD,MAAM,EAAE,GAAG,MAA4B,CAAC;IACxC,IAAI,CAAC,EAAE;QAAE,OAAO,KAAK,CAAC;IACtB,IAAI,EAAE,CAAC,iBAAiB;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC;AAC9D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAmB;IAClD,uEAAuE;IACvE,uEAAuE;IACvE,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9B,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,CAAC,OAAO,GAAG,OAAO,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,CAAC,CAAgB,EAAE,EAAE;YACjC,IAAI,CAAC,CAAC,GAAG,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM;gBAAE,OAAO;YAChE,IAAI,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC;gBAAE,OAAO;YACrC,CAAC,CAAC,cAAc,EAAE,CAAC;YACnB,KAAK,CAAC,OAAO,EAAE,CAAC;QAClB,CAAC,CAAC;QACF,MAAM,CAAC,gBAAgB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC1C,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IAC5D,CAAC,EAAE,EAAE,CAAC,CAAC;AACT,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type RefObject } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* How the visual viewport sits inside the layout viewport, published on
|
|
4
|
+
* `<html>` as `--vv-top` and `--vv-bottom` for the page's sticky edges.
|
|
5
|
+
*
|
|
6
|
+
* An on-screen keyboard shrinks only the visual viewport on iOS Safari (and on
|
|
7
|
+
* Chrome for Android since 108): `innerHeight` keeps its value, so a sticky
|
|
8
|
+
* `bottom: 0` lands behind the keyboard, and dragging the page then pans the
|
|
9
|
+
* visual viewport down the layout one instead of scrolling — the header slides
|
|
10
|
+
* off the top while a composer floats mid-screen. iOS 26 ignores
|
|
11
|
+
* `interactive-widget=resizes-content`, so the edges have to follow by hand.
|
|
12
|
+
*
|
|
13
|
+
* A pinch zoom moves the visual viewport too, and there the sticky edges have
|
|
14
|
+
* to stay where the page put them: both values are zero unless the scale is 1.
|
|
15
|
+
*/
|
|
16
|
+
export declare function visualViewportInsets(): {
|
|
17
|
+
top: number;
|
|
18
|
+
bottom: number;
|
|
19
|
+
};
|
|
20
|
+
/** Fired on `window` whenever the published chrome offsets change. */
|
|
21
|
+
export declare const VIEWPORT_CHROME_EVENT = "orz:viewport-chrome";
|
|
22
|
+
/**
|
|
23
|
+
* Keep `--vv-top` / `--vv-bottom` current. Mount it once, at the app's root;
|
|
24
|
+
* a sticky edge then rides the visual viewport with
|
|
25
|
+
* `top: var(--vv-top, 0px)` or `bottom: var(--vv-bottom, 0px)`, and
|
|
26
|
+
* `useScrollInsets` counts the keyboard whether or not this is mounted.
|
|
27
|
+
*/
|
|
28
|
+
export declare function useVisualViewportInsets(): void;
|
|
29
|
+
/**
|
|
30
|
+
* Slide a sticky header away while the reader scrolls down, and back as they
|
|
31
|
+
* scroll up, the way Safari's own toolbar does — published on `<html>` as
|
|
32
|
+
* `--header-hide`, the number of pixels currently out of view. Only while
|
|
33
|
+
* `enabled`: a phone, where a header is a large share of the screen.
|
|
34
|
+
*
|
|
35
|
+
* The header moves itself: `top: calc(var(--vv-top, 0px) -
|
|
36
|
+
* var(--header-hide, 0px))`, and `data-header-follow` on it, and on anything
|
|
37
|
+
* sticky riding under it, for the settling transition in
|
|
38
|
+
* `@orz/ui/styles.css`. `useScrollInsets` takes `--header-hide` off its top
|
|
39
|
+
* overlays.
|
|
40
|
+
*
|
|
41
|
+
* Only the reader's own scrolling moves it: a landing or an anchor jump is
|
|
42
|
+
* the page placing itself, and arriving to a vanished header would read as a
|
|
43
|
+
* fault. Momentum keeps counting, because each scroll it produces renews the
|
|
44
|
+
* window the touch opened.
|
|
45
|
+
*
|
|
46
|
+
* Once scrolling stops the header settles fully in or fully out, as Safari's
|
|
47
|
+
* does; a header left half-cut reads as broken.
|
|
48
|
+
*/
|
|
49
|
+
export declare function useAutoHideHeader(headerRef: RefObject<HTMLElement | null>, enabled: boolean): void;
|
|
50
|
+
//# sourceMappingURL=viewport-chrome.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport-chrome.d.ts","sourceRoot":"","sources":["../../src/hooks/viewport-chrome.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAa,MAAM,OAAO,CAAC;AAElD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,IAAI;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAOtE;AAED,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,wBAAwB,CAAC;AAM3D;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAyB9C;AAWD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,CAAC,WAAW,GAAG,IAAI,CAAC,EACxC,OAAO,EAAE,OAAO,GACf,IAAI,CAuFN"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { useEffect } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* How the visual viewport sits inside the layout viewport, published on
|
|
4
|
+
* `<html>` as `--vv-top` and `--vv-bottom` for the page's sticky edges.
|
|
5
|
+
*
|
|
6
|
+
* An on-screen keyboard shrinks only the visual viewport on iOS Safari (and on
|
|
7
|
+
* Chrome for Android since 108): `innerHeight` keeps its value, so a sticky
|
|
8
|
+
* `bottom: 0` lands behind the keyboard, and dragging the page then pans the
|
|
9
|
+
* visual viewport down the layout one instead of scrolling — the header slides
|
|
10
|
+
* off the top while a composer floats mid-screen. iOS 26 ignores
|
|
11
|
+
* `interactive-widget=resizes-content`, so the edges have to follow by hand.
|
|
12
|
+
*
|
|
13
|
+
* A pinch zoom moves the visual viewport too, and there the sticky edges have
|
|
14
|
+
* to stay where the page put them: both values are zero unless the scale is 1.
|
|
15
|
+
*/
|
|
16
|
+
export function visualViewportInsets() {
|
|
17
|
+
const vv = window.visualViewport;
|
|
18
|
+
if (vv == null || Math.abs(vv.scale - 1) > 0.01)
|
|
19
|
+
return { top: 0, bottom: 0 };
|
|
20
|
+
return {
|
|
21
|
+
top: Math.max(0, vv.offsetTop),
|
|
22
|
+
bottom: Math.max(0, window.innerHeight - vv.height - vv.offsetTop),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Fired on `window` whenever the published chrome offsets change. */
|
|
26
|
+
export const VIEWPORT_CHROME_EVENT = "orz:viewport-chrome";
|
|
27
|
+
function announce() {
|
|
28
|
+
window.dispatchEvent(new Event(VIEWPORT_CHROME_EVENT));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Keep `--vv-top` / `--vv-bottom` current. Mount it once, at the app's root;
|
|
32
|
+
* a sticky edge then rides the visual viewport with
|
|
33
|
+
* `top: var(--vv-top, 0px)` or `bottom: var(--vv-bottom, 0px)`, and
|
|
34
|
+
* `useScrollInsets` counts the keyboard whether or not this is mounted.
|
|
35
|
+
*/
|
|
36
|
+
export function useVisualViewportInsets() {
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const vv = window.visualViewport;
|
|
39
|
+
if (vv == null)
|
|
40
|
+
return;
|
|
41
|
+
const root = document.documentElement;
|
|
42
|
+
let last = "";
|
|
43
|
+
const publish = () => {
|
|
44
|
+
const { top, bottom } = visualViewportInsets();
|
|
45
|
+
const next = `${top}/${bottom}`;
|
|
46
|
+
if (next === last)
|
|
47
|
+
return;
|
|
48
|
+
last = next;
|
|
49
|
+
root.style.setProperty("--vv-top", `${top}px`);
|
|
50
|
+
root.style.setProperty("--vv-bottom", `${bottom}px`);
|
|
51
|
+
announce();
|
|
52
|
+
};
|
|
53
|
+
publish();
|
|
54
|
+
vv.addEventListener("resize", publish);
|
|
55
|
+
vv.addEventListener("scroll", publish);
|
|
56
|
+
return () => {
|
|
57
|
+
vv.removeEventListener("resize", publish);
|
|
58
|
+
vv.removeEventListener("scroll", publish);
|
|
59
|
+
root.style.removeProperty("--vv-top");
|
|
60
|
+
root.style.removeProperty("--vv-bottom");
|
|
61
|
+
};
|
|
62
|
+
}, []);
|
|
63
|
+
}
|
|
64
|
+
/** Input that marks the scrolling which follows as the reader's own. */
|
|
65
|
+
const READER_INPUT = ["touchstart", "wheel", "keydown", "pointerdown"];
|
|
66
|
+
/** The settling transition's length in styles.css, plus a frame. */
|
|
67
|
+
const SETTLE_MS = 170;
|
|
68
|
+
/** How long after the reader's last input a scroll still counts as theirs. */
|
|
69
|
+
const READER_WINDOW_MS = 600;
|
|
70
|
+
/**
|
|
71
|
+
* Slide a sticky header away while the reader scrolls down, and back as they
|
|
72
|
+
* scroll up, the way Safari's own toolbar does — published on `<html>` as
|
|
73
|
+
* `--header-hide`, the number of pixels currently out of view. Only while
|
|
74
|
+
* `enabled`: a phone, where a header is a large share of the screen.
|
|
75
|
+
*
|
|
76
|
+
* The header moves itself: `top: calc(var(--vv-top, 0px) -
|
|
77
|
+
* var(--header-hide, 0px))`, and `data-header-follow` on it, and on anything
|
|
78
|
+
* sticky riding under it, for the settling transition in
|
|
79
|
+
* `@orz/ui/styles.css`. `useScrollInsets` takes `--header-hide` off its top
|
|
80
|
+
* overlays.
|
|
81
|
+
*
|
|
82
|
+
* Only the reader's own scrolling moves it: a landing or an anchor jump is
|
|
83
|
+
* the page placing itself, and arriving to a vanished header would read as a
|
|
84
|
+
* fault. Momentum keeps counting, because each scroll it produces renews the
|
|
85
|
+
* window the touch opened.
|
|
86
|
+
*
|
|
87
|
+
* Once scrolling stops the header settles fully in or fully out, as Safari's
|
|
88
|
+
* does; a header left half-cut reads as broken.
|
|
89
|
+
*/
|
|
90
|
+
export function useAutoHideHeader(headerRef, enabled) {
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
const header = headerRef.current;
|
|
93
|
+
if (!enabled || header === null)
|
|
94
|
+
return;
|
|
95
|
+
const root = document.documentElement;
|
|
96
|
+
const controller = new AbortController();
|
|
97
|
+
const { signal } = controller;
|
|
98
|
+
let hidden = 0;
|
|
99
|
+
let lastY = window.scrollY;
|
|
100
|
+
let readerUntil = Number.NEGATIVE_INFINITY;
|
|
101
|
+
let touching = false;
|
|
102
|
+
let settle;
|
|
103
|
+
let settled;
|
|
104
|
+
// Never while a finger is down: a drag that pauses is not a drag that
|
|
105
|
+
// ended, and snapping under it would yank the header out of its hand.
|
|
106
|
+
const settleSoon = () => {
|
|
107
|
+
window.clearTimeout(settle);
|
|
108
|
+
if (!touching)
|
|
109
|
+
settle = window.setTimeout(snap, 120);
|
|
110
|
+
};
|
|
111
|
+
const publish = (next, animate) => {
|
|
112
|
+
if (next === hidden)
|
|
113
|
+
return;
|
|
114
|
+
hidden = next;
|
|
115
|
+
root.toggleAttribute("data-header-settling", animate);
|
|
116
|
+
root.style.setProperty("--header-hide", `${hidden}px`);
|
|
117
|
+
};
|
|
118
|
+
const snap = () => {
|
|
119
|
+
const height = header.offsetHeight;
|
|
120
|
+
// Near the top there is no room to keep it hidden: what it covers is
|
|
121
|
+
// the start of the page itself.
|
|
122
|
+
const out = hidden > height / 2 && window.scrollY > height;
|
|
123
|
+
publish(out ? height : 0, true);
|
|
124
|
+
// After the settling transition, not at its start: the scroll insets
|
|
125
|
+
// measure where the header is, and mid-slide it is neither in nor out.
|
|
126
|
+
window.clearTimeout(settled);
|
|
127
|
+
settled = window.setTimeout(announce, SETTLE_MS);
|
|
128
|
+
};
|
|
129
|
+
const onScroll = () => {
|
|
130
|
+
const y = window.scrollY;
|
|
131
|
+
const dy = y - lastY;
|
|
132
|
+
lastY = y;
|
|
133
|
+
const now = performance.now();
|
|
134
|
+
if (now > readerUntil)
|
|
135
|
+
return;
|
|
136
|
+
readerUntil = now + READER_WINDOW_MS;
|
|
137
|
+
const height = header.offsetHeight;
|
|
138
|
+
// Overscroll at either end bounces `scrollY` past the range; the bounce
|
|
139
|
+
// is not the reader asking for the header.
|
|
140
|
+
const max = root.scrollHeight - window.innerHeight;
|
|
141
|
+
if (y < 0 || y > max)
|
|
142
|
+
return;
|
|
143
|
+
publish(Math.min(height, Math.max(0, hidden + dy), Math.max(0, y)), false);
|
|
144
|
+
settleSoon();
|
|
145
|
+
};
|
|
146
|
+
for (const type of READER_INPUT) {
|
|
147
|
+
window.addEventListener(type, () => {
|
|
148
|
+
readerUntil = performance.now() + READER_WINDOW_MS;
|
|
149
|
+
}, { capture: true, passive: true, signal });
|
|
150
|
+
}
|
|
151
|
+
const options = { capture: true, passive: true, signal };
|
|
152
|
+
window.addEventListener("touchstart", () => (touching = true), options);
|
|
153
|
+
for (const type of ["touchend", "touchcancel"]) {
|
|
154
|
+
window.addEventListener(type, () => {
|
|
155
|
+
touching = false;
|
|
156
|
+
settleSoon();
|
|
157
|
+
}, options);
|
|
158
|
+
}
|
|
159
|
+
window.addEventListener("scroll", onScroll, { passive: true, signal });
|
|
160
|
+
return () => {
|
|
161
|
+
controller.abort();
|
|
162
|
+
window.clearTimeout(settle);
|
|
163
|
+
window.clearTimeout(settled);
|
|
164
|
+
root.style.removeProperty("--header-hide");
|
|
165
|
+
root.removeAttribute("data-header-settling");
|
|
166
|
+
announce();
|
|
167
|
+
};
|
|
168
|
+
}, [headerRef, enabled]);
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=viewport-chrome.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewport-chrome.js","sourceRoot":"","sources":["../../src/hooks/viewport-chrome.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-width-table.d.ts","sourceRoot":"","sources":["../../src/lib/cell-width-table.ts"],"names":[],"mappings":"AAGA,yFAAyF;AAEzF,eAAO,MAAM,IAAI,0BAgChB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Generated by scripts/gen-cell-width.mjs from Unicode 16.0.0
|
|
2
|
+
// EastAsianWidth.txt (W and F, adjacent ranges merged). Do not edit by hand.
|
|
3
|
+
/** East Asian Wide and Fullwidth code points, as sorted inclusive `[from, to]` pairs. */
|
|
4
|
+
// biome-ignore format: one range pair per column keeps the table scannable.
|
|
5
|
+
export const WIDE = Uint32Array.of(0x1100, 0x115f, 0x231a, 0x231b, 0x2329, 0x232a, 0x23e9, 0x23ec, 0x23f0, 0x23f0, 0x23f3, 0x23f3, 0x25fd, 0x25fe, 0x2614, 0x2615, 0x2630, 0x2637, 0x2648, 0x2653, 0x267f, 0x267f, 0x268a, 0x268f, 0x2693, 0x2693, 0x26a1, 0x26a1, 0x26aa, 0x26ab, 0x26bd, 0x26be, 0x26c4, 0x26c5, 0x26ce, 0x26ce, 0x26d4, 0x26d4, 0x26ea, 0x26ea, 0x26f2, 0x26f3, 0x26f5, 0x26f5, 0x26fa, 0x26fa, 0x26fd, 0x26fd, 0x2705, 0x2705, 0x270a, 0x270b, 0x2728, 0x2728, 0x274c, 0x274c, 0x274e, 0x274e, 0x2753, 0x2755, 0x2757, 0x2757, 0x2795, 0x2797, 0x27b0, 0x27b0, 0x27bf, 0x27bf, 0x2b1b, 0x2b1c, 0x2b50, 0x2b50, 0x2b55, 0x2b55, 0x2e80, 0x2e99, 0x2e9b, 0x2ef3, 0x2f00, 0x2fd5, 0x2ff0, 0x303e, 0x3041, 0x3096, 0x3099, 0x30ff, 0x3105, 0x312f, 0x3131, 0x318e, 0x3190, 0x31e5, 0x31ef, 0x321e, 0x3220, 0x3247, 0x3250, 0xa48c, 0xa490, 0xa4c6, 0xa960, 0xa97c, 0xac00, 0xd7a3, 0xf900, 0xfaff, 0xfe10, 0xfe19, 0xfe30, 0xfe52, 0xfe54, 0xfe66, 0xfe68, 0xfe6b, 0xff01, 0xff60, 0xffe0, 0xffe6, 0x16fe0, 0x16fe4, 0x16ff0, 0x16ff1, 0x17000, 0x187f7, 0x18800, 0x18cd5, 0x18cff, 0x18d08, 0x1aff0, 0x1aff3, 0x1aff5, 0x1affb, 0x1affd, 0x1affe, 0x1b000, 0x1b122, 0x1b132, 0x1b132, 0x1b150, 0x1b152, 0x1b155, 0x1b155, 0x1b164, 0x1b167, 0x1b170, 0x1b2fb, 0x1d300, 0x1d356, 0x1d360, 0x1d376, 0x1f004, 0x1f004, 0x1f0cf, 0x1f0cf, 0x1f18e, 0x1f18e, 0x1f191, 0x1f19a, 0x1f200, 0x1f202, 0x1f210, 0x1f23b, 0x1f240, 0x1f248, 0x1f250, 0x1f251, 0x1f260, 0x1f265, 0x1f300, 0x1f320, 0x1f32d, 0x1f335, 0x1f337, 0x1f37c, 0x1f37e, 0x1f393, 0x1f3a0, 0x1f3ca, 0x1f3cf, 0x1f3d3, 0x1f3e0, 0x1f3f0, 0x1f3f4, 0x1f3f4, 0x1f3f8, 0x1f43e, 0x1f440, 0x1f440, 0x1f442, 0x1f4fc, 0x1f4ff, 0x1f53d, 0x1f54b, 0x1f54e, 0x1f550, 0x1f567, 0x1f57a, 0x1f57a, 0x1f595, 0x1f596, 0x1f5a4, 0x1f5a4, 0x1f5fb, 0x1f64f, 0x1f680, 0x1f6c5, 0x1f6cc, 0x1f6cc, 0x1f6d0, 0x1f6d2, 0x1f6d5, 0x1f6d7, 0x1f6dc, 0x1f6df, 0x1f6eb, 0x1f6ec, 0x1f6f4, 0x1f6fc, 0x1f7e0, 0x1f7eb, 0x1f7f0, 0x1f7f0, 0x1f90c, 0x1f93a, 0x1f93c, 0x1f945, 0x1f947, 0x1f9ff, 0x1fa70, 0x1fa7c, 0x1fa80, 0x1fa89, 0x1fa8f, 0x1fac6, 0x1face, 0x1fadc, 0x1fadf, 0x1fae9, 0x1faf0, 0x1faf8, 0x20000, 0x2fffd, 0x30000, 0x3fffd);
|
|
6
|
+
//# sourceMappingURL=cell-width-table.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-width-table.js","sourceRoot":"","sources":["../../src/lib/cell-width-table.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,6EAA6E;AAE7E,yFAAyF;AACzF,4EAA4E;AAC5E,MAAM,CAAC,MAAM,IAAI,GAAG,WAAW,CAAC,EAAE,CAChC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAC9D,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAChE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EACtE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CACnC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How many terminal cells a code point takes. Code points are the unit, so a
|
|
3
|
+
* ZWJ sequence counts each of its parts. C0 and C1 controls are the caller's:
|
|
4
|
+
* they come back as 1.
|
|
5
|
+
*/
|
|
6
|
+
export declare function cellWidth(codePoint: number): 0 | 1 | 2;
|
|
7
|
+
//# sourceMappingURL=cell-width.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-width.d.ts","sourceRoot":"","sources":["../../src/lib/cell-width.ts"],"names":[],"mappings":"AAUA;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAStD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { WIDE } from "./cell-width-table.js";
|
|
2
|
+
const ZERO_WIDTH = /^[\p{Mn}\p{Me}]/u;
|
|
3
|
+
const EMOJI = /^\p{Emoji_Presentation}/u;
|
|
4
|
+
// Width + 1 per BMP code point, 0 until first asked: the property tests and
|
|
5
|
+
// the table search cost about 100 ns a call, which non-Latin text would pay
|
|
6
|
+
// on nearly every character.
|
|
7
|
+
let bmp;
|
|
8
|
+
/**
|
|
9
|
+
* How many terminal cells a code point takes. Code points are the unit, so a
|
|
10
|
+
* ZWJ sequence counts each of its parts. C0 and C1 controls are the caller's:
|
|
11
|
+
* they come back as 1.
|
|
12
|
+
*/
|
|
13
|
+
export function cellWidth(codePoint) {
|
|
14
|
+
if (codePoint < 0x300)
|
|
15
|
+
return 1;
|
|
16
|
+
if (codePoint > 0xffff)
|
|
17
|
+
return lookUp(codePoint);
|
|
18
|
+
bmp ??= new Uint8Array(0x10000);
|
|
19
|
+
const known = bmp[codePoint];
|
|
20
|
+
if (known !== 0)
|
|
21
|
+
return (known - 1);
|
|
22
|
+
const width = lookUp(codePoint);
|
|
23
|
+
bmp[codePoint] = width + 1;
|
|
24
|
+
return width;
|
|
25
|
+
}
|
|
26
|
+
function lookUp(codePoint) {
|
|
27
|
+
if (codePoint === 0x200d ||
|
|
28
|
+
(codePoint >= 0xfe00 && codePoint <= 0xfe0f) ||
|
|
29
|
+
(codePoint >= 0xe0100 && codePoint <= 0xe01ef)) {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
const char = String.fromCodePoint(codePoint);
|
|
33
|
+
if (ZERO_WIDTH.test(char))
|
|
34
|
+
return 0;
|
|
35
|
+
if (EMOJI.test(char))
|
|
36
|
+
return 2;
|
|
37
|
+
let lo = 0;
|
|
38
|
+
let hi = WIDE.length / 2 - 1;
|
|
39
|
+
while (lo <= hi) {
|
|
40
|
+
const mid = (lo + hi) >> 1;
|
|
41
|
+
if (codePoint < WIDE[mid * 2])
|
|
42
|
+
hi = mid - 1;
|
|
43
|
+
else if (codePoint > WIDE[mid * 2 + 1])
|
|
44
|
+
lo = mid + 1;
|
|
45
|
+
else
|
|
46
|
+
return 2;
|
|
47
|
+
}
|
|
48
|
+
return 1;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=cell-width.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cell-width.js","sourceRoot":"","sources":["../../src/lib/cell-width.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAE7C,MAAM,UAAU,GAAG,kBAAkB,CAAC;AACtC,MAAM,KAAK,GAAG,0BAA0B,CAAC;AAEzC,4EAA4E;AAC5E,4EAA4E;AAC5E,6BAA6B;AAC7B,IAAI,GAA2B,CAAC;AAEhC;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,SAAiB;IACzC,IAAI,SAAS,GAAG,KAAK;QAAE,OAAO,CAAC,CAAC;IAChC,IAAI,SAAS,GAAG,MAAM;QAAE,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;IACjD,GAAG,KAAK,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;IAChC,MAAM,KAAK,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC;IAC7B,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,CAAC,KAAK,GAAG,CAAC,CAAc,CAAC;IACjD,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IAChC,GAAG,CAAC,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IAC3B,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,MAAM,CAAC,SAAiB;IAC/B,IACE,SAAS,KAAK,MAAM;QACpB,CAAC,SAAS,IAAI,MAAM,IAAI,SAAS,IAAI,MAAM,CAAC;QAC5C,CAAC,SAAS,IAAI,OAAO,IAAI,SAAS,IAAI,OAAO,CAAC,EAC9C,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;IAC7C,IAAI,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACpC,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;QAChB,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,SAAS,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC;YAAE,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;aACvC,IAAI,SAAS,GAAG,IAAI,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;YAAE,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;;YAChD,OAAO,CAAC,CAAC;IAChB,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy and say so. The success message names what was copied — the caller
|
|
3
|
+
* knows that; the failure one does not, because every failure here is the
|
|
4
|
+
* same one: no clipboard to write to. Resolves to whether the copy worked,
|
|
5
|
+
* so a caller can offer a fallback such as selecting the text.
|
|
6
|
+
*/
|
|
7
|
+
export declare function copyToClipboard(text: string, ok: string): Promise<boolean>;
|
|
8
|
+
//# sourceMappingURL=clipboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../src/lib/clipboard.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,OAAO,CAAC,CASlB"}
|