@fusion.dev/helix-headless 0.212.2 → 0.213.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/log/buffer.d.ts +35 -2
- package/dist/log/buffer.js +39 -2
- package/dist/log/index.d.ts +1 -1
- package/dist/log/index.js +1 -0
- package/dist/log/line/log-line.d.ts +34 -3
- package/dist/log/line/log-line.js +41 -5
- package/dist/log/root/log-root.d.ts +27 -3
- package/dist/log/root/log-root.js +24 -8
- package/dist/log/rows.d.ts +4 -2
- package/dist/log/rows.js +11 -7
- package/dist/terminal/keys.d.ts +1 -1
- package/dist/terminal/keys.js +8 -3
- package/dist/terminal/root/terminal-root.d.ts +51 -1
- package/dist/terminal/root/terminal-root.js +101 -3
- package/examples.json +1 -1
- package/lint.json +1 -1
- package/package.json +5 -5
- package/types/jsx.d.ts +242 -10
- package/types/vue.d.ts +62 -2
- package/types/words.d.ts +41 -0
package/dist/terminal/keys.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import { ariaChord, matchesChord } from '@fusion.dev/helix-core';
|
|
2
2
|
const CHORDS = {
|
|
3
|
-
apple: { copy: undefined, find: 'cmd+f', 'tab-focus': 'ctrl+shift+m' },
|
|
4
|
-
other: {
|
|
3
|
+
apple: { copy: undefined, find: 'cmd+f', paste: undefined, 'tab-focus': 'ctrl+shift+m' },
|
|
4
|
+
other: {
|
|
5
|
+
copy: 'ctrl+shift+c',
|
|
6
|
+
find: 'ctrl+shift+f',
|
|
7
|
+
paste: 'ctrl+shift+v',
|
|
8
|
+
'tab-focus': 'ctrl+m',
|
|
9
|
+
},
|
|
5
10
|
};
|
|
6
11
|
export const chordOf = (shortcut, apple) => CHORDS[apple ? 'apple' : 'other'][shortcut];
|
|
7
12
|
export const shortcutOf = (press, apple) => {
|
|
8
|
-
for (const shortcut of ['copy', 'find', 'tab-focus']) {
|
|
13
|
+
for (const shortcut of ['copy', 'find', 'paste', 'tab-focus']) {
|
|
9
14
|
const chord = chordOf(shortcut, apple);
|
|
10
15
|
if (chord !== undefined && matchesChord(press, chord, apple)) {
|
|
11
16
|
return shortcut;
|
|
@@ -7,8 +7,14 @@ import '../../toolbar/index.js';
|
|
|
7
7
|
export interface TerminalFindOptions {
|
|
8
8
|
/** Toward the top of the output, for the previous match. */
|
|
9
9
|
backward: boolean;
|
|
10
|
+
/** Whether case has to match, so "Error" does not find "error". */
|
|
11
|
+
caseSensitive: boolean;
|
|
10
12
|
/** Whether the search is the reader still typing, which stays on the match it has where it still matches. */
|
|
11
13
|
incremental: boolean;
|
|
14
|
+
/** Whether `query` is a regular expression, in JavaScript's syntax, which the frame has checked reads. */
|
|
15
|
+
regex: boolean;
|
|
16
|
+
/** Whether a match has to be a whole word. */
|
|
17
|
+
wholeWord: boolean;
|
|
12
18
|
}
|
|
13
19
|
/** What the frame asks of whatever draws the terminal, as the frame's settings stand. */
|
|
14
20
|
export interface TerminalSettings {
|
|
@@ -120,6 +126,8 @@ export interface TerminalSink {
|
|
|
120
126
|
* Shift+Enter move between the matches, and Escape closes the bar and
|
|
121
127
|
* returns focus to the terminal.
|
|
122
128
|
* - Control+Shift+C copies the selection. On a Mac, Command+C does.
|
|
129
|
+
* - Control+Shift+V pastes, as it does in the browser, and the program reads
|
|
130
|
+
* it as typed text. On a Mac, Command+V does.
|
|
123
131
|
* - Control+M, or Control+Shift+M on a Mac, switches Tab between typing in the
|
|
124
132
|
* terminal and moving focus out of it. The status line says when Tab moves
|
|
125
133
|
* focus, and the terminal's input describes the key to a screen reader.
|
|
@@ -142,11 +150,14 @@ export interface TerminalSink {
|
|
|
142
150
|
* @slot clear-icon - Replaces the glyph inside the clear button.
|
|
143
151
|
* @slot copy-icon - Replaces the glyph inside the copy button.
|
|
144
152
|
* @slot exit-actions - Controls of your own beside Restart, shown once the process exits, such as a button that opens the whole log.
|
|
145
|
-
* @slot find-actions - Controls of your own in the find bar, between
|
|
153
|
+
* @slot find-actions - Controls of your own in the find bar, between its options and the buttons that move between matches.
|
|
146
154
|
* @slot find-close-icon - Replaces the glyph inside the button that closes the find bar.
|
|
147
155
|
* @slot find-icon - Replaces the glyph inside the find button.
|
|
156
|
+
* @slot find-match-case-icon - Replaces the glyph inside the match case button.
|
|
148
157
|
* @slot find-next-icon - Replaces the glyph inside the next match button.
|
|
149
158
|
* @slot find-previous-icon - Replaces the glyph inside the previous match button.
|
|
159
|
+
* @slot find-regex-icon - Replaces the glyph inside the regular expression button.
|
|
160
|
+
* @slot find-whole-word-icon - Replaces the glyph inside the whole word button.
|
|
150
161
|
* @slot fullscreen-icon - Replaces the glyph inside the fullscreen button.
|
|
151
162
|
* @slot label - The terminal's name in the header, such as an icon beside the shell's name. Replaces `label` there.
|
|
152
163
|
* @slot restart-icon - Replaces the glyph inside the Restart button.
|
|
@@ -187,6 +198,11 @@ export interface TerminalSink {
|
|
|
187
198
|
* @csspart find-count - Which match is current and how many there are, such as "3 of 12 matches".
|
|
188
199
|
* @csspart find-field - The search field in the find bar.
|
|
189
200
|
* @csspart find-live - The find button's live region, which speaks while the button is pending.
|
|
201
|
+
* @csspart find-match-case - The button that matches case in the find bar. Pressed while it does.
|
|
202
|
+
* @csspart find-match-case-button - The match case button's inner button.
|
|
203
|
+
* @csspart find-match-case-live - The match case button's live region, which speaks while the button is pending.
|
|
204
|
+
* @csspart find-match-case-spinner - The ring the match case button turns while it is pending.
|
|
205
|
+
* @csspart find-match-case-text - The box the match case button's icon sits in.
|
|
190
206
|
* @csspart find-next - The button that moves to the next match.
|
|
191
207
|
* @csspart find-next-button - The next match button's inner button.
|
|
192
208
|
* @csspart find-next-live - The next match button's live region, which speaks while the button is pending.
|
|
@@ -197,8 +213,18 @@ export interface TerminalSink {
|
|
|
197
213
|
* @csspart find-previous-live - The previous match button's live region, which speaks while the button is pending.
|
|
198
214
|
* @csspart find-previous-spinner - The ring the previous match button turns while it is pending.
|
|
199
215
|
* @csspart find-previous-text - The box the previous match button's icon sits in.
|
|
216
|
+
* @csspart find-regex - The button that reads the find field as a regular expression. Pressed while it does.
|
|
217
|
+
* @csspart find-regex-button - The regular expression button's inner button.
|
|
218
|
+
* @csspart find-regex-live - The regular expression button's live region, which speaks while the button is pending.
|
|
219
|
+
* @csspart find-regex-spinner - The ring the regular expression button turns while it is pending.
|
|
220
|
+
* @csspart find-regex-text - The box the regular expression button's icon sits in.
|
|
200
221
|
* @csspart find-spinner - The ring the find button turns while it is pending.
|
|
201
222
|
* @csspart find-text - The box the find button's icon sits in.
|
|
223
|
+
* @csspart find-whole-word - The button that matches whole words only in the find bar. Pressed while it does.
|
|
224
|
+
* @csspart find-whole-word-button - The whole word button's inner button.
|
|
225
|
+
* @csspart find-whole-word-live - The whole word button's live region, which speaks while the button is pending.
|
|
226
|
+
* @csspart find-whole-word-spinner - The ring the whole word button turns while it is pending.
|
|
227
|
+
* @csspart find-whole-word-text - The box the whole word button's icon sits in.
|
|
202
228
|
* @csspart fullscreen - The button that makes the terminal fill the screen. Pressed while it does.
|
|
203
229
|
* @csspart fullscreen-button - The fullscreen button's inner button.
|
|
204
230
|
* @csspart fullscreen-live - The fullscreen button's live region, which speaks while the button is pending.
|
|
@@ -285,12 +311,32 @@ export declare class HelixHeadlessTerminal extends HelixElement {
|
|
|
285
311
|
findCloseLabel: string;
|
|
286
312
|
/** The name of the find bar's row of buttons, read by screen readers. */
|
|
287
313
|
findControlsLabel: string;
|
|
314
|
+
/** What the find bar says when its regular expression cannot be read. */
|
|
315
|
+
findInvalidLabel: string;
|
|
288
316
|
/** The find button's label, and the find field's name and placeholder. */
|
|
289
317
|
findLabel: string;
|
|
318
|
+
/**
|
|
319
|
+
* Whether the find bar matches case, so "Error" does not find "error". The
|
|
320
|
+
* reader turns it on and off from the bar.
|
|
321
|
+
*/
|
|
322
|
+
findMatchCase: boolean;
|
|
323
|
+
/** The match case button's label. */
|
|
324
|
+
findMatchCaseLabel: string;
|
|
290
325
|
/** The next match button's label. */
|
|
291
326
|
findNextLabel: string;
|
|
292
327
|
/** The previous match button's label. */
|
|
293
328
|
findPreviousLabel: string;
|
|
329
|
+
/**
|
|
330
|
+
* Whether the find bar reads what the reader types as a regular expression,
|
|
331
|
+
* in JavaScript's syntax. The reader turns it on and off from the bar.
|
|
332
|
+
*/
|
|
333
|
+
findRegex: boolean;
|
|
334
|
+
/** The regular expression button's label. */
|
|
335
|
+
findRegexLabel: string;
|
|
336
|
+
/** Whether the find bar matches whole words only. The reader turns it on and off from the bar. */
|
|
337
|
+
findWholeWord: boolean;
|
|
338
|
+
/** The whole word button's label. */
|
|
339
|
+
findWholeWordLabel: string;
|
|
294
340
|
/** The fullscreen button's label. */
|
|
295
341
|
fullscreenLabel: string;
|
|
296
342
|
/** The name of the element the reader types into, which the renderer draws. */
|
|
@@ -336,6 +382,8 @@ export declare class HelixHeadlessTerminal extends HelixElement {
|
|
|
336
382
|
private matchIndex;
|
|
337
383
|
/** How many matches the search found, or `undefined` before one ran. */
|
|
338
384
|
private matchCount;
|
|
385
|
+
/** Whether the regular expression in the field cannot be read, which the count says instead. */
|
|
386
|
+
private invalid;
|
|
339
387
|
/** Whether Tab moves focus, after Control+M. */
|
|
340
388
|
private tabMovesFocus;
|
|
341
389
|
/** Whether any text is selected in the grid. */
|
|
@@ -437,6 +485,8 @@ export declare class HelixHeadlessTerminal extends HelixElement {
|
|
|
437
485
|
private closeFind;
|
|
438
486
|
/** Searches for what the field holds, toward the bottom or with `backward` toward the top. */
|
|
439
487
|
private search;
|
|
488
|
+
/** Turns one of the find bar's options on or off, and searches again with it, staying on a match that still matches. */
|
|
489
|
+
private toggleOption;
|
|
440
490
|
private readonly handleFindInput;
|
|
441
491
|
private readonly handleFindKeys;
|
|
442
492
|
/** Copies the selection, and says so. */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __decorate } from "tslib";
|
|
2
2
|
import { CopyController, customElement, drawIcon, FullscreenController, formatWords, HelixElement, HelixTerminalBellEvent, HelixTerminalInputEvent, HelixTerminalLinkEvent, HelixTerminalResizeEvent, HelixTerminalRestartEvent, HelixTerminalTitleEvent, numberConverter, onApple, stopPress, words, } from '@fusion.dev/helix-core';
|
|
3
|
-
import { arrowClockwise, broom, caretDown, caretUp, copy, cornersOut, magnifyingGlass, x, } from '@fusion.dev/helix-icons';
|
|
3
|
+
import { arrowClockwise, asterisk, broom, caretDown, caretUp, copy, cornersOut, magnifyingGlass, textAa, textAUnderline, x, } from '@fusion.dev/helix-icons';
|
|
4
4
|
import { nothing } from 'lit';
|
|
5
5
|
import { property, state } from 'lit/decorators.js';
|
|
6
6
|
import { keyed } from 'lit/directives/keyed.js';
|
|
@@ -10,6 +10,15 @@ import '../../button/index.js';
|
|
|
10
10
|
import '../../icon-button/index.js';
|
|
11
11
|
import '../../toolbar/index.js';
|
|
12
12
|
import { isTab, shortcutOf, spokenChord } from '../keys.js';
|
|
13
|
+
const readable = (source) => {
|
|
14
|
+
try {
|
|
15
|
+
new RegExp(source);
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
13
22
|
const ANSI = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'];
|
|
14
23
|
const camel = (name) => name.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
15
24
|
const unset = (color) => color === '' || color === 'transparent' || /^rgba\(\s*0,\s*0,\s*0,\s*0\s*\)$/.test(color);
|
|
@@ -27,9 +36,16 @@ let HelixHeadlessTerminal = class HelixHeadlessTerminal extends HelixElement {
|
|
|
27
36
|
this.fallbackLabel = 'Terminal';
|
|
28
37
|
this.findCloseLabel = 'Close find';
|
|
29
38
|
this.findControlsLabel = 'Find controls';
|
|
39
|
+
this.findInvalidLabel = 'Not a valid regular expression';
|
|
30
40
|
this.findLabel = 'Find';
|
|
41
|
+
this.findMatchCase = false;
|
|
42
|
+
this.findMatchCaseLabel = 'Match case';
|
|
31
43
|
this.findNextLabel = 'Next match';
|
|
32
44
|
this.findPreviousLabel = 'Previous match';
|
|
45
|
+
this.findRegex = false;
|
|
46
|
+
this.findRegexLabel = 'Use regular expression';
|
|
47
|
+
this.findWholeWord = false;
|
|
48
|
+
this.findWholeWordLabel = 'Match whole word';
|
|
33
49
|
this.fullscreenLabel = 'Fill the screen';
|
|
34
50
|
this.inputLabel = 'Terminal input';
|
|
35
51
|
this.label = '';
|
|
@@ -44,6 +60,7 @@ let HelixHeadlessTerminal = class HelixHeadlessTerminal extends HelixElement {
|
|
|
44
60
|
this.query = '';
|
|
45
61
|
this.matchIndex = -1;
|
|
46
62
|
this.matchCount = undefined;
|
|
63
|
+
this.invalid = false;
|
|
47
64
|
this.tabMovesFocus = false;
|
|
48
65
|
this.anySelected = false;
|
|
49
66
|
this.hasActions = false;
|
|
@@ -83,6 +100,10 @@ let HelixHeadlessTerminal = class HelixHeadlessTerminal extends HelixElement {
|
|
|
83
100
|
void this.copySelection();
|
|
84
101
|
return;
|
|
85
102
|
}
|
|
103
|
+
if (shortcut === 'paste') {
|
|
104
|
+
event.stopPropagation();
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
86
107
|
if (shortcut === 'tab-focus') {
|
|
87
108
|
event.preventDefault();
|
|
88
109
|
event.stopPropagation();
|
|
@@ -279,6 +300,9 @@ let HelixHeadlessTerminal = class HelixHeadlessTerminal extends HelixElement {
|
|
|
279
300
|
: renderer.find !== undefined;
|
|
280
301
|
}
|
|
281
302
|
get countText() {
|
|
303
|
+
if (this.invalid) {
|
|
304
|
+
return this.findInvalidLabel;
|
|
305
|
+
}
|
|
282
306
|
return this.matchCount === undefined
|
|
283
307
|
? ''
|
|
284
308
|
: formatWords(this, 'matchesLabel', {
|
|
@@ -365,12 +389,28 @@ let HelixHeadlessTerminal = class HelixHeadlessTerminal extends HelixElement {
|
|
|
365
389
|
this.focus();
|
|
366
390
|
}
|
|
367
391
|
search(backward, incremental = false) {
|
|
368
|
-
if (this.query === '') {
|
|
392
|
+
if (this.query === '' || (this.findRegex && !readable(this.query))) {
|
|
393
|
+
this.invalid = this.query !== '';
|
|
369
394
|
this.matchCount = undefined;
|
|
370
395
|
this.renderer?.findEnd?.();
|
|
396
|
+
if (this.invalid) {
|
|
397
|
+
this.say(this.findInvalidLabel);
|
|
398
|
+
}
|
|
371
399
|
return;
|
|
372
400
|
}
|
|
373
|
-
this.
|
|
401
|
+
this.invalid = false;
|
|
402
|
+
this.renderer?.find?.(this.query, {
|
|
403
|
+
backward,
|
|
404
|
+
caseSensitive: this.findMatchCase,
|
|
405
|
+
incremental,
|
|
406
|
+
regex: this.findRegex,
|
|
407
|
+
wholeWord: this.findWholeWord,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
toggleOption(option) {
|
|
411
|
+
this[option] = !this[option];
|
|
412
|
+
this.renderer?.findEnd?.();
|
|
413
|
+
this.search(false, true);
|
|
374
414
|
}
|
|
375
415
|
async copySelection() {
|
|
376
416
|
const text = this.renderer?.selection?.() ?? '';
|
|
@@ -488,6 +528,36 @@ let HelixHeadlessTerminal = class HelixHeadlessTerminal extends HelixElement {
|
|
|
488
528
|
/>
|
|
489
529
|
<span part="find-count" aria-hidden="true">${this.countText}</span>
|
|
490
530
|
<${toolbar} part="find-controls" exportparts="toolbar: find-controls-toolbar" label=${this.findControlsLabel}>
|
|
531
|
+
<${button}
|
|
532
|
+
part="find-match-case"
|
|
533
|
+
exportparts="button: find-match-case-button, live: find-match-case-live, spinner: find-match-case-spinner, text: find-match-case-text"
|
|
534
|
+
aria-pressed=${this.findMatchCase ? 'true' : 'false'}
|
|
535
|
+
label=${this.findMatchCaseLabel}
|
|
536
|
+
@click=${() => this.toggleOption('findMatchCase')}
|
|
537
|
+
@helix-press=${stopPress}
|
|
538
|
+
>
|
|
539
|
+
<slot name="find-match-case-icon">${drawIcon(textAa)}</slot>
|
|
540
|
+
</${button}>
|
|
541
|
+
<${button}
|
|
542
|
+
part="find-whole-word"
|
|
543
|
+
exportparts="button: find-whole-word-button, live: find-whole-word-live, spinner: find-whole-word-spinner, text: find-whole-word-text"
|
|
544
|
+
aria-pressed=${this.findWholeWord ? 'true' : 'false'}
|
|
545
|
+
label=${this.findWholeWordLabel}
|
|
546
|
+
@click=${() => this.toggleOption('findWholeWord')}
|
|
547
|
+
@helix-press=${stopPress}
|
|
548
|
+
>
|
|
549
|
+
<slot name="find-whole-word-icon">${drawIcon(textAUnderline)}</slot>
|
|
550
|
+
</${button}>
|
|
551
|
+
<${button}
|
|
552
|
+
part="find-regex"
|
|
553
|
+
exportparts="button: find-regex-button, live: find-regex-live, spinner: find-regex-spinner, text: find-regex-text"
|
|
554
|
+
aria-pressed=${this.findRegex ? 'true' : 'false'}
|
|
555
|
+
label=${this.findRegexLabel}
|
|
556
|
+
@click=${() => this.toggleOption('findRegex')}
|
|
557
|
+
@helix-press=${stopPress}
|
|
558
|
+
>
|
|
559
|
+
<slot name="find-regex-icon">${drawIcon(asterisk)}</slot>
|
|
560
|
+
</${button}>
|
|
491
561
|
<slot name="find-actions" part="find-actions"></slot>
|
|
492
562
|
<${button}
|
|
493
563
|
part="find-previous"
|
|
@@ -615,10 +685,21 @@ __decorate([
|
|
|
615
685
|
words,
|
|
616
686
|
property({ attribute: 'find-controls-label' })
|
|
617
687
|
], HelixHeadlessTerminal.prototype, "findControlsLabel", void 0);
|
|
688
|
+
__decorate([
|
|
689
|
+
words,
|
|
690
|
+
property({ attribute: 'find-invalid-label' })
|
|
691
|
+
], HelixHeadlessTerminal.prototype, "findInvalidLabel", void 0);
|
|
618
692
|
__decorate([
|
|
619
693
|
words,
|
|
620
694
|
property({ attribute: 'find-label' })
|
|
621
695
|
], HelixHeadlessTerminal.prototype, "findLabel", void 0);
|
|
696
|
+
__decorate([
|
|
697
|
+
property({ attribute: 'find-match-case', type: Boolean })
|
|
698
|
+
], HelixHeadlessTerminal.prototype, "findMatchCase", void 0);
|
|
699
|
+
__decorate([
|
|
700
|
+
words,
|
|
701
|
+
property({ attribute: 'find-match-case-label' })
|
|
702
|
+
], HelixHeadlessTerminal.prototype, "findMatchCaseLabel", void 0);
|
|
622
703
|
__decorate([
|
|
623
704
|
words,
|
|
624
705
|
property({ attribute: 'find-next-label' })
|
|
@@ -627,6 +708,20 @@ __decorate([
|
|
|
627
708
|
words,
|
|
628
709
|
property({ attribute: 'find-previous-label' })
|
|
629
710
|
], HelixHeadlessTerminal.prototype, "findPreviousLabel", void 0);
|
|
711
|
+
__decorate([
|
|
712
|
+
property({ attribute: 'find-regex', type: Boolean })
|
|
713
|
+
], HelixHeadlessTerminal.prototype, "findRegex", void 0);
|
|
714
|
+
__decorate([
|
|
715
|
+
words,
|
|
716
|
+
property({ attribute: 'find-regex-label' })
|
|
717
|
+
], HelixHeadlessTerminal.prototype, "findRegexLabel", void 0);
|
|
718
|
+
__decorate([
|
|
719
|
+
property({ attribute: 'find-whole-word', type: Boolean })
|
|
720
|
+
], HelixHeadlessTerminal.prototype, "findWholeWord", void 0);
|
|
721
|
+
__decorate([
|
|
722
|
+
words,
|
|
723
|
+
property({ attribute: 'find-whole-word-label' })
|
|
724
|
+
], HelixHeadlessTerminal.prototype, "findWholeWordLabel", void 0);
|
|
630
725
|
__decorate([
|
|
631
726
|
words,
|
|
632
727
|
property({ attribute: 'fullscreen-label' })
|
|
@@ -679,6 +774,9 @@ __decorate([
|
|
|
679
774
|
__decorate([
|
|
680
775
|
state()
|
|
681
776
|
], HelixHeadlessTerminal.prototype, "matchCount", void 0);
|
|
777
|
+
__decorate([
|
|
778
|
+
state()
|
|
779
|
+
], HelixHeadlessTerminal.prototype, "invalid", void 0);
|
|
682
780
|
__decorate([
|
|
683
781
|
state()
|
|
684
782
|
], HelixHeadlessTerminal.prototype, "tabMovesFocus", void 0);
|