@fusion.dev/helix 0.220.0 → 0.222.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/api.json +1 -1
- package/custom-elements.json +1 -1
- package/dist/code-editor/root/code-editor-root.d.ts +2 -2
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/index.js +1 -0
- package/dist/editor/root/editor-root.css.d.ts +1 -0
- package/dist/editor/root/editor-root.css.js +2 -0
- package/dist/editor/root/editor-root.d.ts +62 -0
- package/dist/editor/root/editor-root.js +33 -0
- package/dist/element-events.generated.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/sandbox/index.d.ts +2 -1
- package/dist/sandbox/preview/sandbox-preview.d.ts +46 -8
- package/dist/sandbox/preview/sandbox-preview.js +65 -11
- package/dist/shared/dictation/dictation.css.js +1 -1
- package/dist/textarea/root/textarea-root.css.js +1 -1
- package/dist/textarea/root/textarea-root.js +2 -0
- package/examples.json +1 -1
- package/lint.json +1 -1
- package/package.json +6 -6
- package/skills/helix/SKILL.md +2 -2
- package/types/jsx.d.ts +1130 -20
- package/types/svelte.d.ts +1 -0
- package/types/vue.d.ts +229 -2
- package/types/words.d.ts +248 -0
|
@@ -12,7 +12,7 @@ import '../../toolbar/index.js';
|
|
|
12
12
|
* header, a find bar and a status line each a strip on the raised surface,
|
|
13
13
|
* one frame around them. It is 24rem tall unless `fill` is set or
|
|
14
14
|
* `--code-editor-block-size` says otherwise. An engine draws inside it:
|
|
15
|
-
* `@fusion.dev/helix-editor` themes CodeMirror's gutter, caret, selection and
|
|
15
|
+
* `@fusion.dev/helix-code-editor` themes CodeMirror's gutter, caret, selection and
|
|
16
16
|
* syntax from your tokens, and draws a problem's line number in its tone.
|
|
17
17
|
*
|
|
18
18
|
* Set `fill` for an editor that fills a pane, such as one side of a split
|
|
@@ -20,7 +20,7 @@ import '../../toolbar/index.js';
|
|
|
20
20
|
*
|
|
21
21
|
* @since 0.216.0
|
|
22
22
|
* @status experimental
|
|
23
|
-
* @category
|
|
23
|
+
* @category editors
|
|
24
24
|
* @aliases Code editor, CodeMirror, File editor, IDE, Monaco
|
|
25
25
|
* @documentation https://fusion.dev/helix/components/code-editor
|
|
26
26
|
*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/editor-root.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './root/editor-root.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const styles: import("lit").CSSResult;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { createSheet } from '@fusion.dev/helix-core';
|
|
2
|
+
export const styles = createSheet(`:host {display: flex;flex-direction: column}:host([fill]) {inset: 0;position: absolute}[part="region"] {background-color: var(--editor-background-color, var(--helix-color-background));border: var(--helix-border-thin) solid var(--editor-border-color, var(--helix-color-border));border-radius: var(--editor-radius, var(--helix-radii-lg));box-sizing: border-box;display: flex;flex: 1 1 auto;flex-direction: column;min-block-size: var(--editor-min-block-size, 12rem);overflow: clip;transition: border-color var(--helix-duration-fast) var(--helix-easing-standard)}:host([fill]) [part="region"] {block-size: 100%;min-block-size: 0}:host([separation="plain"]) [part="region"] {border: none;border-radius: 0}:host(:state(document-focused):not([separation="plain"])) [part="region"] {border-color: var(--helix-color-primary);outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);outline-offset: 0}[part="header"] {--helix-size-icon: var(--helix-size-icon-sm);--helix-size-icon-loose: calc(var(--helix-size-icon) * 1.2);--helix-size-icon-tight: calc(var(--helix-size-icon) * 0.8);--helix-size-control: var(--helix-size-control-sm);--helix-size-control-loose: calc(var(--helix-size-control) * 1.2);--helix-size-control-tight: calc(var(--helix-size-control) * 0.8);background-color: var(--editor-header-background-color, var(--helix-color-surface-raised));border-block-end: var(--helix-border-thin) solid var(--editor-border-color, var(--helix-color-border));box-sizing: border-box;container-type: inline-size;display: flex;flex: 0 0 auto;flex-direction: column;gap: var(--helix-space-2xs);inset-block-start: var(--editor-header-offset, 0);padding: var(--helix-space-2xs) var(--helix-space-xs);position: sticky;z-index: 1}[part="header"][hidden] {display: none}[part="controls"] {--toolbar-separator-gap: var(--helix-space-2xs);display: flex}[part="controls"]::part(toolbar) {flex: 1 1 auto;flex-wrap: wrap;row-gap: var(--helix-space-2xs)}[part="tools"] {display: flex;gap: var(--helix-space-2xs)}[part="divider"] {align-self: center;block-size: calc(var(--helix-size-control) * 0.6)}@container (inline-size < 40rem) {[part="controls"] {--toolbar-gap: var(--helix-space-sm)}[part="divider"] {display: none}}[aria-pressed="true"], :host(:state(find-open)) [part="find-toggle"] {--icon-button-background-color: color-mix(in oklab, var(--helix-color-foreground) 12%, transparent);--icon-button-foreground-color: var(--helix-color-foreground)}[part="block-name"] {display: inline-grid;justify-items: start}[part="block-name"] > span {grid-area: 1 / 1}[part="block-name"] > [hidden] {display: block;visibility: hidden}[part="actions"] {display: flex;gap: var(--helix-space-2xs);margin-inline-start: auto}[part="actions"][hidden] {display: none}[part="link-bar"] {--helix-size-control: calc(var(--helix-size-control-sm) * 0.8);--helix-size-control-loose: calc(var(--helix-size-control) * 1.2);--helix-size-control-tight: calc(var(--helix-size-control) * 0.8);align-items: center;display: flex;gap: var(--helix-space-2xs);min-inline-size: 0;padding-inline-start: var(--helix-space-2xs)}[part="link-bar"][hidden] {display: none}[part="link-field"] {appearance: none;background-color: var(--editor-background-color, var(--helix-color-background));block-size: var(--helix-size-control);border: var(--helix-border-thin) solid var(--editor-border-color, var(--helix-color-border));border-radius: var(--helix-radii-sm);box-sizing: border-box;color: var(--helix-color-foreground);flex: 1 1 12rem;font-family: var(--helix-font-sans);font-size: max(var(--helix-text-field-min), var(--helix-text-sm));max-inline-size: 28rem;min-inline-size: 0;padding-inline: var(--helix-space-xs)}[part="link-field"]:focus-visible {outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);outline-offset: var(--helix-size-focus-offset)}[part="link-controls"] {flex: 0 0 auto}[part="find"] {--find-background-color: transparent;--find-border-color: var(--editor-border-color, var(--helix-color-border));--find-field-background-color: var(--editor-background-color, var(--helix-color-background))}[part="find"]:not(:state(open)) {display: none}[part="find"]::part(bar) {border-block-end: none;padding-block: 0;padding-inline: var(--helix-space-2xs) 0}[part="viewport"] {color: var(--helix-color-foreground);display: flex;flex: 1 1 auto;flex-direction: column;min-block-size: 0}:host([fill]) [part="viewport"] {overflow: auto}[part="viewport"] ::slotted(*) {box-sizing: border-box;caret-color: var(--helix-color-foreground);flex: 1 1 auto;font-family: var(--helix-font-sans);font-size: var(--helix-text-md);line-height: var(--helix-leading-normal);outline: none;padding-block: var(--editor-padding, var(--helix-space-lg));padding-inline: max(var(--editor-padding, var(--helix-space-lg)), calc((100% - var(--editor-measure, 72ch)) / 2))}[part="footer"] {color: var(--helix-color-foreground-muted);font-family: var(--helix-font-sans);font-size: var(--helix-text-xs)}@media print {[part="header"] {display: none}[part="region"], :host([fill]) [part="viewport"] {overflow: visible}}`);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { HelixHeadlessEditor } from '@fusion.dev/helix-headless/editor';
|
|
2
|
+
import type { CSSResultGroup } from 'lit';
|
|
3
|
+
import '../../button/index.js';
|
|
4
|
+
import '../../divider/index.js';
|
|
5
|
+
import '../../find/index.js';
|
|
6
|
+
import '../../icon-button/index.js';
|
|
7
|
+
import '../../menu/index.js';
|
|
8
|
+
import '../../toolbar/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* @summary A frame around a document that ProseMirror or another editor draws.
|
|
11
|
+
*
|
|
12
|
+
* @description
|
|
13
|
+
* Drawn as Code Editor is: the document on the page's ground, and the toolbar
|
|
14
|
+
* a strip on the raised surface, one frame around them. The strip stays at the
|
|
15
|
+
* top of the page while a long document scrolls under it, and the frame grows
|
|
16
|
+
* with the document from `--editor-min-block-size`. Set `fill` for an editor
|
|
17
|
+
* that fills a pane, where the document scrolls inside the frame.
|
|
18
|
+
*
|
|
19
|
+
* The document's headings, lists, quotes, code, and tables are drawn by the
|
|
20
|
+
* base layer's `data-prose` rules, so it reads as your docs and chat replies
|
|
21
|
+
* do. Write `data-prose` on the document so the base layer draws it before a
|
|
22
|
+
* renderer connects, and `connectEditor` writes it too. Its lines keep to a
|
|
23
|
+
* readable length, `--editor-measure`, centered in a wide frame.
|
|
24
|
+
*
|
|
25
|
+
* @since 0.221.0
|
|
26
|
+
* @status experimental
|
|
27
|
+
* @category editors
|
|
28
|
+
* @aliases Rich text editor, WYSIWYG, Document editor, ProseMirror, Block editor
|
|
29
|
+
* @documentation https://fusion.dev/helix/components/editor
|
|
30
|
+
*
|
|
31
|
+
* @cssproperty --editor-background-color - The ground the document is drawn on. Defaults to the page's background.
|
|
32
|
+
* @cssproperty --editor-border-color - The frame's edge and the line under the toolbar.
|
|
33
|
+
* @cssproperty --editor-header-background-color - The fill of the toolbar's strip.
|
|
34
|
+
* @cssproperty --editor-header-offset - How far below the top of the page the toolbar stays while the document scrolls, such as the height of your own fixed header. `0` unless set.
|
|
35
|
+
* @cssproperty --editor-measure - The longest a line of the document runs, as a length: `72ch` unless set.
|
|
36
|
+
* @cssproperty --editor-min-block-size - The frame's least height, which it grows from with the document: `12rem` unless set. Ignored with `fill`.
|
|
37
|
+
* @cssproperty --editor-padding - The space around the document, inside the frame.
|
|
38
|
+
* @cssproperty --editor-radius - The frame's corner radius.
|
|
39
|
+
*/
|
|
40
|
+
export declare class HelixEditor extends HelixHeadlessEditor {
|
|
41
|
+
static tier: string;
|
|
42
|
+
/**
|
|
43
|
+
* Covers the container instead of growing with the document, for an editor
|
|
44
|
+
* that fills a pane, such as one side of a split view. The document scrolls
|
|
45
|
+
* inside the frame. The container needs a position, such as
|
|
46
|
+
* `position: relative`.
|
|
47
|
+
*/
|
|
48
|
+
fill: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* How the frame stands off the page. Unset, it is outlined. `plain` draws
|
|
51
|
+
* no edge or corners, for a document that fills a page or a pane of its own.
|
|
52
|
+
*/
|
|
53
|
+
separation?: 'outlined' | 'plain';
|
|
54
|
+
/** The block menu's button is drawn as the tools beside it are. */
|
|
55
|
+
protected get blockEmphasis(): 'ghost' | undefined;
|
|
56
|
+
static styles: CSSResultGroup;
|
|
57
|
+
}
|
|
58
|
+
declare global {
|
|
59
|
+
interface HTMLElementTagNameMap {
|
|
60
|
+
'helix-editor': HelixEditor;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { __decorate } from "tslib";
|
|
2
|
+
import { customElement } from '@fusion.dev/helix-core';
|
|
3
|
+
import { HelixHeadlessEditor } from '@fusion.dev/helix-headless/editor';
|
|
4
|
+
import { property } from 'lit/decorators.js';
|
|
5
|
+
import { styles as glyphStyles } from '../../shared/glyph/glyph.css.js';
|
|
6
|
+
import { styles } from './editor-root.css.js';
|
|
7
|
+
import '../../button/index.js';
|
|
8
|
+
import '../../divider/index.js';
|
|
9
|
+
import '../../find/index.js';
|
|
10
|
+
import '../../icon-button/index.js';
|
|
11
|
+
import '../../menu/index.js';
|
|
12
|
+
import '../../toolbar/index.js';
|
|
13
|
+
let HelixEditor = class HelixEditor extends HelixHeadlessEditor {
|
|
14
|
+
constructor() {
|
|
15
|
+
super(...arguments);
|
|
16
|
+
this.fill = false;
|
|
17
|
+
}
|
|
18
|
+
static { this.tier = 'helix-'; }
|
|
19
|
+
get blockEmphasis() {
|
|
20
|
+
return 'ghost';
|
|
21
|
+
}
|
|
22
|
+
static { this.styles = [glyphStyles, styles]; }
|
|
23
|
+
};
|
|
24
|
+
__decorate([
|
|
25
|
+
property({ reflect: true, type: Boolean })
|
|
26
|
+
], HelixEditor.prototype, "fill", void 0);
|
|
27
|
+
__decorate([
|
|
28
|
+
property({ reflect: true })
|
|
29
|
+
], HelixEditor.prototype, "separation", void 0);
|
|
30
|
+
HelixEditor = __decorate([
|
|
31
|
+
customElement('helix-editor')
|
|
32
|
+
], HelixEditor);
|
|
33
|
+
export { HelixEditor };
|
|
@@ -134,6 +134,7 @@ registerElementEvents({
|
|
|
134
134
|
'helix-drawer': ['close', 'helix-before-close', 'helix-before-open', 'toggle'],
|
|
135
135
|
'helix-drilldown': ['helix-navigate', 'helix-select'],
|
|
136
136
|
'helix-drilldown-item': ['helix-select'],
|
|
137
|
+
'helix-editor': ['helix-found'],
|
|
137
138
|
'helix-file-input': ['change', 'helix-before-change', 'helix-reject', 'input'],
|
|
138
139
|
'helix-filter-chip': ['helix-dismiss'],
|
|
139
140
|
'helix-filter-input': [
|
package/dist/index.d.ts
CHANGED
|
@@ -43,6 +43,7 @@ export * from './divider/index.js';
|
|
|
43
43
|
export * from './donut-chart/index.js';
|
|
44
44
|
export * from './drawer/index.js';
|
|
45
45
|
export * from './drilldown/index.js';
|
|
46
|
+
export * from './editor/index.js';
|
|
46
47
|
export * from './empty/index.js';
|
|
47
48
|
export * from './file-input/index.js';
|
|
48
49
|
export * from './filter-input/index.js';
|
package/dist/index.js
CHANGED
|
@@ -38,6 +38,7 @@ export * from './divider/index.js';
|
|
|
38
38
|
export * from './donut-chart/index.js';
|
|
39
39
|
export * from './drawer/index.js';
|
|
40
40
|
export * from './drilldown/index.js';
|
|
41
|
+
export * from './editor/index.js';
|
|
41
42
|
export * from './empty/index.js';
|
|
42
43
|
export * from './file-input/index.js';
|
|
43
44
|
export * from './filter-input/index.js';
|
package/dist/sandbox/index.d.ts
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
*
|
|
11
11
|
* What makes this a sandbox rather than a layout is here: `Sandbox.Preview`
|
|
12
12
|
* hardcodes `sandbox="allow-scripts allow-forms"` on its frame and never
|
|
13
|
-
* `allow-same-origin`, which is the pairing that would undo it.
|
|
13
|
+
* `allow-same-origin`, which is the pairing that would undo it. The frame is
|
|
14
|
+
* granted clipboard write, fullscreen, and the microphone.
|
|
14
15
|
*/
|
|
15
16
|
export { registerCompilerWorker } from './compile.js';
|
|
16
17
|
export * from './console/sandbox-console.js';
|
|
@@ -44,7 +44,23 @@ export interface PreviewSize {
|
|
|
44
44
|
size: number;
|
|
45
45
|
type: 'helix-preview-size';
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
/**
|
|
48
|
+
* The frame asking this page to listen. Its own origin is opaque, so the
|
|
49
|
+
* recognizer there refuses to start.
|
|
50
|
+
*/
|
|
51
|
+
export interface PreviewSpeechStart {
|
|
52
|
+
/** Which listening this is, so a late reply cannot land in the next one. */
|
|
53
|
+
id: number;
|
|
54
|
+
/** A BCP 47 tag, such as `en-US`. */
|
|
55
|
+
lang: string;
|
|
56
|
+
type: 'helix-speech-start';
|
|
57
|
+
}
|
|
58
|
+
/** The frame asking this page to stop the listening `id` names. */
|
|
59
|
+
export interface PreviewSpeechStop {
|
|
60
|
+
id: number;
|
|
61
|
+
type: 'helix-speech-stop';
|
|
62
|
+
}
|
|
63
|
+
export type PreviewMessage = PreviewConsole | PreviewFetch | PreviewPress | PreviewReady | PreviewRender | PreviewRendered | PreviewSize | PreviewSpeechStart | PreviewSpeechStop;
|
|
48
64
|
/**
|
|
49
65
|
* @summary A frame that renders sandbox source in a separate document.
|
|
50
66
|
*
|
|
@@ -263,6 +279,10 @@ export declare class HelixSandboxPreview extends HelixElement {
|
|
|
263
279
|
styles: string;
|
|
264
280
|
/** Whether the frame has said it is listening. */
|
|
265
281
|
private ready;
|
|
282
|
+
/** The recognizer running for the frame, on this page. */
|
|
283
|
+
private speech;
|
|
284
|
+
/** Which ask `speech` belongs to. */
|
|
285
|
+
private speechId;
|
|
266
286
|
/** The source the frame was last sent, which it holds and does not run again. */
|
|
267
287
|
private sentSource;
|
|
268
288
|
/** The source the frame held when it last reported an uncaught error. */
|
|
@@ -403,17 +423,35 @@ export declare class HelixSandboxPreview extends HelixElement {
|
|
|
403
423
|
* where the browser refused to open one.
|
|
404
424
|
*
|
|
405
425
|
* **The tab runs the example as this preview does**: in a frame that fills
|
|
406
|
-
* it, with this frame's `sandbox`, loading `src` or the built-in
|
|
407
|
-
* and sent the example once it loads. The example stays on an
|
|
408
|
-
* and the tab's `opener` is cleared, so it cannot reach this
|
|
409
|
-
* shows the example as it was when it opened, and edits after
|
|
410
|
-
* reach it. A short inline script in the tab sends the example,
|
|
411
|
-
* whose Content Security Policy refuses inline scripts the tab
|
|
412
|
-
* as the built-in document does there.
|
|
426
|
+
* it, with this frame's `sandbox` and `allow`, loading `src` or the built-in
|
|
427
|
+
* document, and sent the example once it loads. The example stays on an
|
|
428
|
+
* opaque origin, and the tab's `opener` is cleared, so it cannot reach this
|
|
429
|
+
* page. The tab shows the example as it was when it opened, and edits after
|
|
430
|
+
* that do not reach it. A short inline script in the tab sends the example,
|
|
431
|
+
* so on a page whose Content Security Policy refuses inline scripts the tab
|
|
432
|
+
* opens empty, as the built-in document does there. Dictation in the tab
|
|
433
|
+
* listens through the tab's own recognizer, asked from this page.
|
|
413
434
|
*/
|
|
414
435
|
open(): Window | null;
|
|
415
436
|
connectedCallback(): void;
|
|
416
437
|
disconnectedCallback(): void;
|
|
438
|
+
/**
|
|
439
|
+
* Takes a frame's ask to start or stop listening, and returns whether the
|
|
440
|
+
* message was one. `scope` is the window that holds the frame, whose
|
|
441
|
+
* recognizer listens.
|
|
442
|
+
*/
|
|
443
|
+
private speak;
|
|
444
|
+
/**
|
|
445
|
+
* Listens for a frame that cannot listen for itself, and sends each guess
|
|
446
|
+
* back to that frame.
|
|
447
|
+
*
|
|
448
|
+
* **An engine this page registered listens in its place**, so a page that
|
|
449
|
+
* gives its own fields another engine gives it to its examples too. The
|
|
450
|
+
* browser's recognizer listens in `scope`, the window that holds the frame:
|
|
451
|
+
* a tab `open()` makes holds its own. A registered engine runs in this page,
|
|
452
|
+
* so it listens only for the frame this page holds.
|
|
453
|
+
*/
|
|
454
|
+
private hear;
|
|
417
455
|
/**
|
|
418
456
|
* Loads the frame's document again, and re-sends the example to it.
|
|
419
457
|
*
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var HelixSandboxPreview_1;
|
|
2
2
|
import { __decorate } from "tslib";
|
|
3
|
-
import { customElement, DelayController, HelixConsoleEvent, HelixElement, HelixResizeEvent, words, } from '@fusion.dev/helix-core';
|
|
3
|
+
import { browserSpeech, customElement, DelayController, HelixConsoleEvent, HelixElement, HelixResizeEvent, pageSpeechEngine, recognizeSpeech, words, } from '@fusion.dev/helix-core';
|
|
4
4
|
const SETTLE = 120;
|
|
5
5
|
const PAINT_PATIENCE = 1000;
|
|
6
6
|
import { html, nothing } from 'lit';
|
|
@@ -10,6 +10,7 @@ import { defaultFrame, withScript } from '../frame.js';
|
|
|
10
10
|
import { isFetch, RELAY_OPEN, relayAddress, relayFetch, relayPrefixes, } from '../relay.js';
|
|
11
11
|
import { styles as sheet } from './sandbox-preview.css.js';
|
|
12
12
|
const SANDBOX = 'allow-scripts allow-forms';
|
|
13
|
+
const ALLOW = 'clipboard-write *; fullscreen *; microphone *';
|
|
13
14
|
const RELATIVE = /(\bfrom\s*|\bimport\s*\(?\s*)(["'])(\.{1,2}\/[^"']+)\2/g;
|
|
14
15
|
const moduleText = (path, source) => `data:text/javascript;charset=utf-8,${encodeURIComponent(source.replace(RELATIVE, (_, lead, quote, specifier) => `${lead}${quote}${new URL(specifier, `sandbox:/${path}`).href}${quote}`))}`;
|
|
15
16
|
let HelixSandboxPreview = class HelixSandboxPreview extends HelixElement {
|
|
@@ -40,6 +41,9 @@ let HelixSandboxPreview = class HelixSandboxPreview extends HelixElement {
|
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
const data = event.data;
|
|
44
|
+
if (this.speak(event, window)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
43
47
|
if (isFetch(data)) {
|
|
44
48
|
void this.answer(data, event.source);
|
|
45
49
|
return;
|
|
@@ -191,6 +195,7 @@ let HelixSandboxPreview = class HelixSandboxPreview extends HelixElement {
|
|
|
191
195
|
tab.document.title = this.label;
|
|
192
196
|
body.style.margin = '0';
|
|
193
197
|
const frame = tab.document.createElement('iframe');
|
|
198
|
+
frame.setAttribute('allow', ALLOW);
|
|
194
199
|
frame.setAttribute('sandbox', SANDBOX);
|
|
195
200
|
frame.title = this.label;
|
|
196
201
|
frame.style.cssText = 'block-size: 100vh; border: none; display: block; inline-size: 100vw';
|
|
@@ -200,15 +205,18 @@ let HelixSandboxPreview = class HelixSandboxPreview extends HelixElement {
|
|
|
200
205
|
else {
|
|
201
206
|
frame.src = this.frame?.src || this.src;
|
|
202
207
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
208
|
+
const relays = relayPrefixes(this.relay).length > 0;
|
|
209
|
+
tab.addEventListener('message', (event) => {
|
|
210
|
+
if (event.source !== frame.contentWindow || event.origin !== 'null') {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
if (this.speak(event, tab)) {
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (relays && isFetch(event.data)) {
|
|
217
|
+
void this.answer(event.data, event.source);
|
|
218
|
+
}
|
|
219
|
+
});
|
|
212
220
|
const send = tab.document.createElement('script');
|
|
213
221
|
send.textContent = `const frame = document.currentScript.previousElementSibling
|
|
214
222
|
frame.onload = () => frame.contentWindow.postMessage(${JSON.stringify(this.message)}, '*')`;
|
|
@@ -222,6 +230,52 @@ frame.onload = () => frame.contentWindow.postMessage(${JSON.stringify(this.messa
|
|
|
222
230
|
disconnectedCallback() {
|
|
223
231
|
super.disconnectedCallback();
|
|
224
232
|
window.removeEventListener('message', this.handleMessage);
|
|
233
|
+
this.speech?.stop();
|
|
234
|
+
this.speech = undefined;
|
|
235
|
+
}
|
|
236
|
+
speak(event, scope) {
|
|
237
|
+
const data = event.data;
|
|
238
|
+
if (data?.type === 'helix-speech-start') {
|
|
239
|
+
this.hear(data, event.source, scope, event.ports[0]);
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
if (data?.type === 'helix-speech-stop') {
|
|
243
|
+
if (this.speechId === data.id) {
|
|
244
|
+
this.speech?.stop();
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
hear(ask, source, scope, port) {
|
|
251
|
+
this.speech?.stop();
|
|
252
|
+
this.speechId = ask.id;
|
|
253
|
+
const tell = (kind, text, error, level) => {
|
|
254
|
+
const message = { error, id: ask.id, kind, level, text, type: 'helix-speech-heard' };
|
|
255
|
+
if (port) {
|
|
256
|
+
port.postMessage(message);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
source.postMessage(message, '*');
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
const listener = {
|
|
263
|
+
end: (reason, message) => {
|
|
264
|
+
tell(reason === 'error' ? 'error' : reason === 'stopped' ? 'stopped' : 'end', undefined, message);
|
|
265
|
+
if (this.speechId === ask.id) {
|
|
266
|
+
this.speech = undefined;
|
|
267
|
+
this.speechId = undefined;
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
final: (text) => tell('final', text),
|
|
271
|
+
level: (level) => tell('level', undefined, undefined, level),
|
|
272
|
+
partial: (text) => tell('partial', text),
|
|
273
|
+
};
|
|
274
|
+
const engine = pageSpeechEngine();
|
|
275
|
+
this.speech =
|
|
276
|
+
engine !== browserSpeech && scope === window
|
|
277
|
+
? engine.listen({ lang: ask.lang, listener })
|
|
278
|
+
: recognizeSpeech(ask.lang, listener, { meter: false, scope });
|
|
225
279
|
}
|
|
226
280
|
reload() {
|
|
227
281
|
const frame = this.frame;
|
|
@@ -286,7 +340,7 @@ frame.onload = () => frame.contentWindow.postMessage(${JSON.stringify(this.messa
|
|
|
286
340
|
srcdoc=${this.src === '' ? defaultFrame(this.map) : nothing}
|
|
287
341
|
title=${this.label}
|
|
288
342
|
aria-describedby=${this.failure === undefined ? nothing : 'error'}
|
|
289
|
-
allow
|
|
343
|
+
allow=${ALLOW}
|
|
290
344
|
sandbox=${SANDBOX}
|
|
291
345
|
></iframe>${this.failure === undefined
|
|
292
346
|
? nothing
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createSheet } from '@fusion.dev/helix-core';
|
|
2
|
-
export const styles = createSheet(`[part~="voice-listening"] {box-shadow: 0 0 0 calc(var(--dictation-level, 0.4) * var(--helix-space-2xs) * 1.5) color-mix(in oklab, var(--helix-color-
|
|
2
|
+
export const styles = createSheet(`[part~="voice-listening"]:not(:disabled), [part~="voice-listening"]:hover:not(:disabled) {background-color: var(--helix-color-success-surface);box-shadow: 0 0 0 calc(var(--dictation-level, 0.4) * var(--helix-space-2xs) * 1.5) color-mix(in oklab, var(--helix-color-success) 35%, transparent);color: var(--helix-color-foreground-on-status);transition: box-shadow var(--helix-duration-fast) linear}[part~="voice-listening"]:hover:not(:disabled) {background-color: var(--helix-color-success-surface-hover)}@media (prefers-reduced-motion: reduce) {[part~="voice-listening"]:not(:disabled), [part~="voice-listening"]:hover:not(:disabled) {box-shadow: 0 0 0 calc(var(--helix-space-2xs) * 0.6) color-mix(in oklab, var(--helix-color-success) 35%, transparent);transition: none}}@media print {[part~="voice"] {display: none}}`);
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { createSheet } from '@fusion.dev/helix-core';
|
|
2
|
-
export const styles = createSheet(`[part="field"] {background-color: var(--textarea-background-color, var(--helix-color-well));border: var(--helix-border-thin) solid var(--textarea-border-color, var(--helix-color-border));border-radius: var(--textarea-radius, var(--helix-radii-sm));display: flex;gap: var(--helix-space-2xs);padding: var(--textarea-padding, var(--helix-space-xs));transition: border-color var(--helix-duration-fast) var(--helix-easing-standard), background-color var(--helix-duration-fast) var(--helix-easing-standard)}[part="control"] {line-height: var(--helix-leading-normal);overflow-wrap: break-word;resize: var(--textarea-resize, vertical)}slot[name="start"]::slotted(*), slot[name="end"]::slotted(*) {align-self: flex-start;color: var(--helix-color-foreground-muted);flex: 0 0 auto}[part="clear"], [part~="voice"] {--icon-size: var(--helix-size-icon-tight);all: unset;align-items: center;align-self: flex-start;border-radius: var(--helix-radii-sm);box-sizing: border-box;color: var(--helix-color-foreground-muted);cursor: pointer;display: flex;flex: 0 0 auto;justify-content: center;min-block-size: var(--helix-size-icon-loose);min-inline-size: var(--helix-size-icon-loose);transition: color var(--helix-duration-fast) var(--helix-easing-standard)}[part="clear"]:hover, [part~="voice"]:hover {color: var(--helix-color-foreground)}[part="clear"]:focus-visible, [part~="voice"]:focus-visible {outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);outline-offset: var(--helix-size-focus-offset)}:host([auto-grow]) [part="control"] {overflow: hidden;resize: none}:host(:disabled) [part="field"] {cursor: not-allowed;opacity: 0.5}:host([readonly]) [part="field"] {background-color: transparent}@media print {:host {print-color-adjust: exact}}`);
|
|
2
|
+
export const styles = createSheet(`[part="field"] {background-color: var(--textarea-background-color, var(--helix-color-well));border: var(--helix-border-thin) solid var(--textarea-border-color, var(--helix-color-border));border-radius: var(--textarea-radius, var(--helix-radii-sm));display: flex;gap: var(--helix-space-2xs);padding: var(--textarea-padding, var(--helix-space-xs));position: relative;transition: border-color var(--helix-duration-fast) var(--helix-easing-standard), background-color var(--helix-duration-fast) var(--helix-easing-standard)}[part="control"] {line-height: var(--helix-leading-normal);overflow-wrap: break-word;resize: var(--textarea-resize, vertical)}slot[name="start"]::slotted(*), slot[name="end"]::slotted(*) {align-self: flex-start;color: var(--helix-color-foreground-muted);flex: 0 0 auto}[part="clear"], [part~="voice"] {--icon-size: var(--helix-size-icon-tight);all: unset;align-items: center;align-self: flex-start;border-radius: var(--helix-radii-sm);box-sizing: border-box;color: var(--helix-color-foreground-muted);cursor: pointer;display: flex;flex: 0 0 auto;justify-content: center;min-block-size: var(--helix-size-icon-loose);min-inline-size: var(--helix-size-icon-loose);transition: color var(--helix-duration-fast) var(--helix-easing-standard)}[part="clear"]:hover, [part~="voice"]:hover {color: var(--helix-color-foreground)}[part="clear"]:focus-visible, [part~="voice"]:focus-visible {outline: var(--helix-size-focus-ring) solid var(--helix-color-focus-ring);outline-offset: var(--helix-size-focus-offset)}[part~="voice"] {background-color: transparent;block-size: var(--helix-size-icon-loose);inline-size: var(--helix-size-icon-loose);inset-block-start: calc(var(--textarea-padding, var(--helix-space-xs)) + (1em * var(--helix-leading-normal) - var(--helix-size-icon-loose)) / 2);inset-inline-end: var(--textarea-padding, var(--helix-space-xs));overflow: hidden;position: absolute}[part="field"]:has([part~="voice"]) [part="control"] {padding-inline-end: calc(var(--helix-size-icon-loose) + var(--helix-space-2xs))}[part~="voice"]:hover {background-color: color-mix(in oklab, var(--helix-color-foreground) 8%, transparent)}:host([auto-grow]) [part="control"] {overflow: hidden;resize: none}:host(:disabled) [part="field"] {cursor: not-allowed;opacity: 0.5}:host([readonly]) [part="field"] {background-color: transparent}@media print {:host {print-color-adjust: exact}}`);
|
|
@@ -5,9 +5,11 @@ import { styles as dictationStyles } from '../../shared/dictation/dictation.css.
|
|
|
5
5
|
import { styles as fieldStyles } from '../../shared/field/field.css.js';
|
|
6
6
|
import { styles as fieldFrameStyles } from '../../shared/field/field-frame.css.js';
|
|
7
7
|
import { styles as fieldStackedStyles } from '../../shared/field/field-stacked.css.js';
|
|
8
|
+
import { styles as glyphStyles } from '../../shared/glyph/glyph.css.js';
|
|
8
9
|
import { styles } from './textarea-root.css.js';
|
|
9
10
|
let HelixTextarea = class HelixTextarea extends HelixHeadlessTextarea {
|
|
10
11
|
static { this.styles = [
|
|
12
|
+
glyphStyles,
|
|
11
13
|
fieldStyles,
|
|
12
14
|
fieldFrameStyles,
|
|
13
15
|
fieldStackedStyles,
|