@gjermundgaraba-designs/pixel-paper 0.22.2 → 0.24.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/DESIGN.md +18 -17
- package/dist/src/adapters/mermaid.d.ts.map +1 -1
- package/dist/src/adapters/mermaid.js +0 -24
- package/dist/src/announcer/announcer.d.ts.map +1 -1
- package/dist/src/announcer/announcer.js +12 -12
- package/dist/src/board/board.d.ts +0 -10
- package/dist/src/board/board.d.ts.map +1 -1
- package/dist/src/board/board.js +36 -64
- package/dist/src/board/index.d.ts +2 -1
- package/dist/src/board/index.d.ts.map +1 -1
- package/dist/src/board/index.js +2 -1
- package/dist/src/button/button.d.ts +1 -1
- package/dist/src/button/button.d.ts.map +1 -1
- package/dist/src/commandPalette/commandPalette.d.ts.map +1 -1
- package/dist/src/commandPalette/commandPalette.js +78 -83
- package/dist/src/commandPalette/keys.d.ts +2 -2
- package/dist/src/commandPalette/keys.d.ts.map +1 -1
- package/dist/src/commandPalette/keys.js +2 -4
- package/dist/src/commandPalette/match.d.ts.map +1 -1
- package/dist/src/commandPalette/match.js +2 -2
- package/dist/src/confirmDialog/confirmDialog.d.ts.map +1 -1
- package/dist/src/confirmDialog/confirmDialog.js +1 -2
- package/dist/src/copyButton/copyButton.d.ts +0 -5
- package/dist/src/copyButton/copyButton.d.ts.map +1 -1
- package/dist/src/copyButton/copyButton.js +2 -2
- package/dist/src/crumbs/crumbs.d.ts +0 -2
- package/dist/src/crumbs/crumbs.d.ts.map +1 -1
- package/dist/src/crumbs/crumbs.js +2 -2
- package/dist/src/dialog/dialog.d.ts +2 -0
- package/dist/src/dialog/dialog.d.ts.map +1 -1
- package/dist/src/dialog/dialog.js +3 -2
- package/dist/src/drag/drag.d.ts +28 -0
- package/dist/src/drag/drag.d.ts.map +1 -1
- package/dist/src/drag/drag.js +39 -17
- package/dist/src/editableTitle/editableTitle.d.ts +4 -4
- package/dist/src/field/field.d.ts +7 -1
- package/dist/src/field/field.d.ts.map +1 -1
- package/dist/src/field/field.js +14 -6
- package/dist/src/fieldset/fieldset.d.ts.map +1 -1
- package/dist/src/fieldset/fieldset.js +2 -4
- package/dist/src/fileTree/fileTree.d.ts.map +1 -1
- package/dist/src/fileTree/fileTree.js +16 -16
- package/dist/src/iconButton/iconButton.d.ts +6 -1
- package/dist/src/iconButton/iconButton.d.ts.map +1 -1
- package/dist/src/iconButton/iconButton.js +7 -2
- package/dist/src/inlineInput/inlineInput.d.ts +4 -0
- package/dist/src/inlineInput/inlineInput.d.ts.map +1 -1
- package/dist/src/inlineInput/inlineInput.js +9 -6
- package/dist/src/keyHelp/index.d.ts +1 -1
- package/dist/src/keyHelp/index.d.ts.map +1 -1
- package/dist/src/keyHelp/index.js +1 -1
- package/dist/src/keyHelp/keyHelp.d.ts +1 -3
- package/dist/src/keyHelp/keyHelp.d.ts.map +1 -1
- package/dist/src/keyHelp/keyHelp.js +3 -6
- package/dist/src/keyHint/keyLabel.d.ts +2 -0
- package/dist/src/keyHint/keyLabel.d.ts.map +1 -1
- package/dist/src/keyHint/keyLabel.js +3 -2
- package/dist/src/lineDiff/diff.d.ts +6 -3
- package/dist/src/lineDiff/diff.d.ts.map +1 -1
- package/dist/src/lineDiff/diff.js +40 -28
- package/dist/src/lineDiff/patch.d.ts.map +1 -1
- package/dist/src/lineDiff/patch.js +18 -13
- package/dist/src/linkState/linkState.d.ts.map +1 -1
- package/dist/src/linkState/linkState.js +1 -7
- package/dist/src/menu/slip.js +2 -2
- package/dist/src/meter/meter.d.ts.map +1 -1
- package/dist/src/meter/meter.js +22 -17
- package/dist/src/notice/notice.d.ts +2 -0
- package/dist/src/notice/notice.d.ts.map +1 -1
- package/dist/src/notice/notice.js +2 -1
- package/dist/src/panel/panel.d.ts +3 -0
- package/dist/src/panel/panel.d.ts.map +1 -1
- package/dist/src/panel/panel.js +2 -1
- package/dist/src/passwordField/passwordField.d.ts.map +1 -1
- package/dist/src/passwordField/passwordField.js +3 -7
- package/dist/src/rail/index.d.ts +1 -1
- package/dist/src/rail/index.d.ts.map +1 -1
- package/dist/src/rail/index.js +1 -1
- package/dist/src/rail/rail.d.ts +95 -20
- package/dist/src/rail/rail.d.ts.map +1 -1
- package/dist/src/rail/rail.js +103 -46
- package/dist/src/reel/layout.d.ts +9 -5
- package/dist/src/reel/layout.d.ts.map +1 -1
- package/dist/src/reel/layout.js +11 -5
- package/dist/src/reel/reel.d.ts.map +1 -1
- package/dist/src/reel/reel.js +122 -68
- package/dist/src/scheme/scheme.d.ts +1 -1
- package/dist/src/scheme/scheme.d.ts.map +1 -1
- package/dist/src/segmented/segmented.d.ts +0 -3
- package/dist/src/segmented/segmented.d.ts.map +1 -1
- package/dist/src/segmented/segmented.js +1 -6
- package/dist/src/selectField/selectField.d.ts.map +1 -1
- package/dist/src/selectField/selectField.js +3 -10
- package/dist/src/sortable/index.d.ts +1 -1
- package/dist/src/sortable/index.d.ts.map +1 -1
- package/dist/src/sortable/index.js +1 -1
- package/dist/src/sortable/sortable.d.ts +39 -40
- package/dist/src/sortable/sortable.d.ts.map +1 -1
- package/dist/src/sortable/sortable.js +14 -31
- package/dist/src/stack/stack.d.ts +1 -1
- package/dist/src/stack/stack.d.ts.map +1 -1
- package/dist/src/stack/stack.js +1 -1
- package/dist/src/tabs/tabs.d.ts +1 -1
- package/dist/src/tabs/tabs.d.ts.map +1 -1
- package/dist/src/tabs/tabs.js +1 -1
- package/dist/src/textareaField/textareaField.d.ts.map +1 -1
- package/dist/src/textareaField/textareaField.js +3 -7
- package/dist/src/time/time.d.ts.map +1 -1
- package/dist/src/time/time.js +4 -2
- package/dist/src/tips/index.d.ts +1 -1
- package/dist/src/tips/index.d.ts.map +1 -1
- package/dist/src/tips/tips.d.ts +30 -1
- package/dist/src/tips/tips.d.ts.map +1 -1
- package/dist/src/tips/tips.js +53 -31
- package/dist/src/toast/index.d.ts +1 -1
- package/dist/src/toast/index.d.ts.map +1 -1
- package/dist/src/toast/toast.d.ts +5 -5
- package/dist/src/toast/toast.d.ts.map +1 -1
- package/dist/src/toast/toast.js +1 -6
- package/dist/src/toolbar/toolbar.d.ts +0 -3
- package/dist/src/toolbar/toolbar.d.ts.map +1 -1
- package/dist/src/toolbar/toolbar.js +1 -2
- package/package.json +1 -1
- package/src/adapters/mermaid.ts +0 -24
- package/src/announcer/announcer.ts +13 -17
- package/src/board/board.ts +32 -80
- package/src/board/index.ts +1 -1
- package/src/button/button.ts +1 -1
- package/src/commandPalette/commandPalette.ts +21 -38
- package/src/commandPalette/keys.ts +2 -4
- package/src/commandPalette/match.ts +3 -3
- package/src/confirmDialog/confirmDialog.ts +5 -3
- package/src/copyButton/copyButton.ts +2 -5
- package/src/crumbs/crumbs.ts +2 -4
- package/src/dialog/dialog.ts +3 -2
- package/src/drag/drag.ts +54 -5
- package/src/field/field.ts +29 -7
- package/src/fieldset/fieldset.ts +2 -4
- package/src/fileTree/fileTree.ts +10 -12
- package/src/iconButton/iconButton.ts +19 -2
- package/src/inlineInput/inlineInput.ts +11 -2
- package/src/keyHelp/index.ts +0 -1
- package/src/keyHelp/keyHelp.ts +3 -9
- package/src/keyHint/keyLabel.ts +5 -2
- package/src/lineDiff/diff.ts +70 -54
- package/src/lineDiff/patch.ts +27 -14
- package/src/linkState/linkState.ts +1 -8
- package/src/menu/slip.ts +2 -2
- package/src/meter/meter.ts +25 -15
- package/src/notice/notice.ts +2 -1
- package/src/panel/panel.ts +5 -0
- package/src/passwordField/passwordField.ts +3 -7
- package/src/pixel-paper.css +200 -134
- package/src/rail/index.ts +2 -0
- package/src/rail/rail.ts +124 -44
- package/src/reel/layout.ts +14 -7
- package/src/reel/reel.ts +132 -63
- package/src/scheme/scheme.ts +1 -1
- package/src/segmented/segmented.ts +1 -9
- package/src/selectField/selectField.ts +3 -10
- package/src/sortable/index.ts +0 -1
- package/src/sortable/sortable.ts +35 -54
- package/src/stack/stack.ts +1 -1
- package/src/tabs/tabs.ts +1 -1
- package/src/textareaField/textareaField.ts +3 -7
- package/src/time/time.ts +6 -7
- package/src/tips/index.ts +1 -1
- package/src/tips/tips.ts +71 -35
- package/src/toast/index.ts +1 -1
- package/src/toast/toast.ts +2 -8
- package/src/tokens.css +7 -0
- package/src/toolbar/toolbar.ts +0 -5
- package/dist/src/inlineInput/inline.d.ts +0 -6
- package/dist/src/inlineInput/inline.d.ts.map +0 -1
- package/dist/src/inlineInput/inline.js +0 -7
- package/src/inlineInput/inline.ts +0 -13
package/src/field/field.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Input as UiInput } from "@foldkit/ui";
|
|
2
|
-
import type { Html, HtmlBuilder, TextareaAttribute } from "foldkit/html";
|
|
2
|
+
import type { Attribute, Html, HtmlBuilder, TextareaAttribute } from "foldkit/html";
|
|
3
3
|
|
|
4
4
|
import type { Attributes } from "../attributes/attributes.ts";
|
|
5
5
|
|
|
@@ -28,6 +28,32 @@ export const describedByError = <Message>(
|
|
|
28
28
|
? []
|
|
29
29
|
: [h.AriaDescribedBy(hasHelp ? `${errorId(id)} ${UiInput.descriptionId(id)}` : errorId(id))];
|
|
30
30
|
|
|
31
|
+
/** A field's control, after the `@foldkit/ui` attributes: described by its error before its
|
|
32
|
+
* help line, and `aria-required` when the form is not sent without it. */
|
|
33
|
+
// NOTE: @foldkit/ui gap: Input, Select and Textarea have no required option, so the wrapper
|
|
34
|
+
// says it.
|
|
35
|
+
export const controlAttributes = <Message>(
|
|
36
|
+
id: string,
|
|
37
|
+
error: string | undefined,
|
|
38
|
+
help: string | undefined,
|
|
39
|
+
isRequired: boolean,
|
|
40
|
+
h: HtmlBuilder<Message>,
|
|
41
|
+
): ReadonlyArray<TextareaAttribute<Message>> => [
|
|
42
|
+
...describedByError(id, error, help !== undefined, h),
|
|
43
|
+
...(isRequired ? [h.AriaRequired(true)] : []),
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
// HELP
|
|
47
|
+
|
|
48
|
+
/** The sans sentence under a field's controls, given one, with the `@foldkit/ui`
|
|
49
|
+
* `description` group that makes it describe them. */
|
|
50
|
+
export const helpView = <Message>(
|
|
51
|
+
help: string | undefined,
|
|
52
|
+
description: ReadonlyArray<Attribute<Message>>,
|
|
53
|
+
h: HtmlBuilder<Message>,
|
|
54
|
+
): ReadonlyArray<Html> =>
|
|
55
|
+
help === undefined ? [] : [h.p([...description, h.Class("field-help")], [help])];
|
|
56
|
+
|
|
31
57
|
// VIEW
|
|
32
58
|
|
|
33
59
|
/** Configuration for rendering a text field with `view`. */
|
|
@@ -92,17 +118,13 @@ export const view = <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Messag
|
|
|
92
118
|
[h.Class(`field${isBoxed || type === "date" ? " boxed" : ""}${isMono ? " mono" : ""}`)],
|
|
93
119
|
[
|
|
94
120
|
h.label([...groups.label, ...(isLabelHidden ? [h.Class("sr-only")] : [])], [label]),
|
|
95
|
-
// NOTE: @foldkit/ui gap: Input has no required option, so the wrapper says it.
|
|
96
121
|
h.input([
|
|
97
122
|
...inputAttributes,
|
|
98
123
|
...groups.input,
|
|
99
|
-
...
|
|
100
|
-
...(isRequired ? [h.AriaRequired(true)] : []),
|
|
124
|
+
...controlAttributes(id, error, help, isRequired, h),
|
|
101
125
|
]),
|
|
102
126
|
...errorView(id, error, h),
|
|
103
|
-
...(help
|
|
104
|
-
? []
|
|
105
|
-
: [h.p([...groups.description, h.Class("field-help")], [help])]),
|
|
127
|
+
...helpView(help, groups.description, h),
|
|
106
128
|
],
|
|
107
129
|
),
|
|
108
130
|
},
|
package/src/fieldset/fieldset.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Fieldset as UiFieldset } from "@foldkit/ui";
|
|
2
2
|
import type { Html, HtmlBuilder } from "foldkit/html";
|
|
3
3
|
|
|
4
|
-
import { describedByError, errorView } from "../field/field.ts";
|
|
4
|
+
import { describedByError, errorView, helpView } from "../field/field.ts";
|
|
5
5
|
|
|
6
6
|
// VIEW
|
|
7
7
|
|
|
@@ -43,9 +43,7 @@ export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html
|
|
|
43
43
|
h.legend([...groups.legend, h.Class("field-label")], [label]),
|
|
44
44
|
...body,
|
|
45
45
|
...errorView(id, error, h),
|
|
46
|
-
...(help
|
|
47
|
-
? []
|
|
48
|
-
: [h.p([...groups.description, h.Class("field-help")], [help])]),
|
|
46
|
+
...helpView(help, groups.description, h),
|
|
49
47
|
],
|
|
50
48
|
),
|
|
51
49
|
},
|
package/src/fileTree/fileTree.ts
CHANGED
|
@@ -209,18 +209,16 @@ const moveInTree = (tree: Element, event: KeyboardEvent): Option.Option<TreeKeyA
|
|
|
209
209
|
const items = ownItems(tree);
|
|
210
210
|
|
|
211
211
|
return pipe(
|
|
212
|
-
Array.
|
|
213
|
-
Option.flatMap((index) =>
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}),
|
|
223
|
-
),
|
|
212
|
+
Array.findFirstWithIndex(items, (item) => item === event.target),
|
|
213
|
+
Option.flatMap(([item, index]) => {
|
|
214
|
+
if (isTypeahead(event)) {
|
|
215
|
+
return moveTo(itemStartingWith(items, index, event.key));
|
|
216
|
+
} else if (isPlain(event)) {
|
|
217
|
+
return actOnKey(items, index, item, event.key);
|
|
218
|
+
} else {
|
|
219
|
+
return Option.none();
|
|
220
|
+
}
|
|
221
|
+
}),
|
|
224
222
|
);
|
|
225
223
|
};
|
|
226
224
|
|
|
@@ -2,8 +2,10 @@ import { Button as UiButton } from "@foldkit/ui";
|
|
|
2
2
|
import type { Html, HtmlBuilder } from "foldkit/html";
|
|
3
3
|
|
|
4
4
|
import type { Attributes } from "../attributes/attributes.ts";
|
|
5
|
+
import type { KeyHintConfig } from "../button/button.ts";
|
|
5
6
|
import * as Glyph from "../glyph/glyph.ts";
|
|
6
7
|
import type { GlyphName } from "../glyph/glyphs.ts";
|
|
8
|
+
import { keyLabel } from "../keyHint/keyLabel.ts";
|
|
7
9
|
|
|
8
10
|
// VIEW
|
|
9
11
|
|
|
@@ -17,13 +19,25 @@ export type ViewConfig<Message> = Readonly<{
|
|
|
17
19
|
isQuiet?: boolean;
|
|
18
20
|
/** Unavailable but focusable (`aria-disabled`), as in `@foldkit/ui`. */
|
|
19
21
|
isDisabled?: boolean;
|
|
22
|
+
/** The key that does the same, printed in the tip after the label (`data-tip-key`): the
|
|
23
|
+
* button has no words to print it beside. */
|
|
24
|
+
keyHint?: KeyHintConfig;
|
|
20
25
|
/** Further attributes for the `<button>`, never a class. */
|
|
21
26
|
attributes?: Attributes<Message>;
|
|
22
27
|
}>;
|
|
23
28
|
|
|
24
|
-
/** A bare glyph button, 24px square. Its label is its accessible name and its tip
|
|
29
|
+
/** A bare glyph button, 24px square. Its label is its accessible name and its tip, and its
|
|
30
|
+
* key, where it has one, prints in the tip after the label. */
|
|
25
31
|
export const view = <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Message>): Html => {
|
|
26
|
-
const {
|
|
32
|
+
const {
|
|
33
|
+
glyph,
|
|
34
|
+
label,
|
|
35
|
+
onClick,
|
|
36
|
+
isQuiet = false,
|
|
37
|
+
isDisabled = false,
|
|
38
|
+
keyHint,
|
|
39
|
+
attributes = [],
|
|
40
|
+
} = config;
|
|
27
41
|
|
|
28
42
|
return UiButton.view(
|
|
29
43
|
{
|
|
@@ -36,6 +50,9 @@ export const view = <Message>(config: ViewConfig<Message>, h: HtmlBuilder<Messag
|
|
|
36
50
|
...groups.button,
|
|
37
51
|
h.AriaLabel(label),
|
|
38
52
|
h.DataAttribute("tip", label),
|
|
53
|
+
...(keyHint === undefined
|
|
54
|
+
? []
|
|
55
|
+
: [h.DataAttribute("tip-key", keyLabel(keyHint.keys, keyHint.platform))]),
|
|
39
56
|
h.Class(isQuiet ? "iconbtn quiet" : "iconbtn"),
|
|
40
57
|
],
|
|
41
58
|
[Glyph.view({ name: glyph }, h)],
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Input as UiInput } from "@foldkit/ui";
|
|
2
|
-
import { Effect, Match, Option, Schema } from "effect";
|
|
2
|
+
import { Effect, Match, Option, Schema, String, pipe } from "effect";
|
|
3
3
|
import * as Command from "foldkit/command";
|
|
4
4
|
import * as Dom from "foldkit/dom";
|
|
5
5
|
import { type Html, type HtmlBuilder } from "foldkit/html";
|
|
@@ -12,7 +12,6 @@ import * as Update from "foldkit/update";
|
|
|
12
12
|
|
|
13
13
|
import { isPlain } from "../keys/keys.ts";
|
|
14
14
|
import * as Notice from "../notice/notice.ts";
|
|
15
|
-
import { toWrite } from "./inline.ts";
|
|
16
15
|
|
|
17
16
|
// MODEL
|
|
18
17
|
|
|
@@ -141,6 +140,16 @@ const end = (model: Model, how: InlineEnd): UpdateReturn => ({
|
|
|
141
140
|
outMessage: OutMessage.Ended({ how, maybeLastSavedId: model.maybeLastSavedId }),
|
|
142
141
|
});
|
|
143
142
|
|
|
143
|
+
/** What Enter or leaving an inline field writes: the draft trimmed, or none (an untouched
|
|
144
|
+
* draft, so an edit made elsewhere to `value` is never overwritten; an empty one; or the
|
|
145
|
+
* text it replaces). */
|
|
146
|
+
export const toWrite = (maybeDraft: Option.Option<string>, value: string): Option.Option<string> =>
|
|
147
|
+
pipe(
|
|
148
|
+
maybeDraft,
|
|
149
|
+
Option.map(String.trim),
|
|
150
|
+
Option.filter((text) => String.isNonEmpty(text) && text !== value),
|
|
151
|
+
);
|
|
152
|
+
|
|
144
153
|
const commit = (model: Model, trigger: SaveTrigger): UpdateReturn =>
|
|
145
154
|
Option.match(toWrite(model.maybeDraft, model.replaced), {
|
|
146
155
|
onNone: () => end(model, trigger),
|
package/src/keyHelp/index.ts
CHANGED
package/src/keyHelp/keyHelp.ts
CHANGED
|
@@ -13,7 +13,7 @@ import * as Card from "../card/card.ts";
|
|
|
13
13
|
import * as Dialog from "../dialog/dialog.ts";
|
|
14
14
|
import { foldDialog } from "../dialog/fold.ts";
|
|
15
15
|
import * as Field from "../field/field.ts";
|
|
16
|
-
import { type KeyPlatform, keyLabel } from "../keyHint/keyLabel.ts";
|
|
16
|
+
import { type KeyPlatform, keyLabel, pressesOf } from "../keyHint/keyLabel.ts";
|
|
17
17
|
import * as Keymap from "../keymap/keymap.ts";
|
|
18
18
|
|
|
19
19
|
// MODEL
|
|
@@ -54,7 +54,7 @@ export type Message = typeof Message.Type;
|
|
|
54
54
|
// OUT MESSAGE
|
|
55
55
|
|
|
56
56
|
/** Union of OutMessages the key help reports: the reader closed it (Escape, a click on the
|
|
57
|
-
* veil, a close button). Opening it, by `?` or `open`,
|
|
57
|
+
* veil, a close button). Opening it, by `?` or `open`, reports nothing. */
|
|
58
58
|
export const OutMessage = defineMessageUnion({
|
|
59
59
|
Closed: {},
|
|
60
60
|
});
|
|
@@ -99,9 +99,6 @@ export const update = (model: Model, message: Message) =>
|
|
|
99
99
|
export const open = (model: Model): Update.Return<Model, Message> =>
|
|
100
100
|
dialogFold.open(clearQuery(model));
|
|
101
101
|
|
|
102
|
-
/** Closes the key help without reporting `Closed`. */
|
|
103
|
-
export const close = (model: Model): Update.Return<Model, Message> => dialogFold.close(model);
|
|
104
|
-
|
|
105
102
|
// ACCESSORS
|
|
106
103
|
|
|
107
104
|
/** The key help's id, unique in the document. */
|
|
@@ -134,12 +131,9 @@ export const subscriptions = Subscription.make<Model, Message>()((entry) => ({
|
|
|
134
131
|
|
|
135
132
|
// LISTING
|
|
136
133
|
|
|
137
|
-
const keysOf = (keys: KeySequence): ReadonlyArray<string> =>
|
|
138
|
-
Predicate.isString(keys) ? [keys] : keys;
|
|
139
|
-
|
|
140
134
|
const isFound = (binding: Binding, labels: string, query: string): boolean =>
|
|
141
135
|
pipe(
|
|
142
|
-
[labels, ...Array.flatMap(binding.keys,
|
|
136
|
+
[labels, ...Array.flatMap(binding.keys, pressesOf), binding.does, binding.group],
|
|
143
137
|
Array.join(" "),
|
|
144
138
|
String.toLowerCase,
|
|
145
139
|
String.includes(query),
|
package/src/keyHint/keyLabel.ts
CHANGED
|
@@ -110,6 +110,10 @@ const pressLabel = (press: string, platform: KeyPlatform): string => {
|
|
|
110
110
|
return `${prefix}${keyName(key, Array.isReadonlyArrayNonEmpty(orderedModifiers))}`;
|
|
111
111
|
};
|
|
112
112
|
|
|
113
|
+
/** The presses a binding's keys name: one, or a sequence. */
|
|
114
|
+
export const pressesOf = (keys: KeySequence): ReadonlyArray<string> =>
|
|
115
|
+
Predicate.isString(keys) ? [keys] : keys;
|
|
116
|
+
|
|
113
117
|
/**
|
|
114
118
|
* A key binding as the help and the key hints print it. Takes the same strings
|
|
115
119
|
* `Dom.streamFromKeyBindings` matches: `"Mod+K"` prints `⌘K` on Apple and `Ctrl+K` elsewhere,
|
|
@@ -119,8 +123,7 @@ const pressLabel = (press: string, platform: KeyPlatform): string => {
|
|
|
119
123
|
* named key keeps its spaces.
|
|
120
124
|
*/
|
|
121
125
|
export const keyLabel = (keys: KeySequence, platform: KeyPlatform): string => {
|
|
122
|
-
const
|
|
123
|
-
const labels = Array.map(presses, (press) => pressLabel(press, platform));
|
|
126
|
+
const labels = Array.map(pressesOf(keys), (press) => pressLabel(press, platform));
|
|
124
127
|
|
|
125
128
|
return Array.every(labels, isCharacter) ? Array.join(labels, "") : Array.join(labels, " ");
|
|
126
129
|
};
|
package/src/lineDiff/diff.ts
CHANGED
|
@@ -49,6 +49,14 @@ const numberLine = (numbers: Numbers, kind: LineKind, text: string): [Numbers, D
|
|
|
49
49
|
Match.exhaustive,
|
|
50
50
|
);
|
|
51
51
|
|
|
52
|
+
type Line = Readonly<{ kind: LineKind; text: string }>;
|
|
53
|
+
|
|
54
|
+
const numbered = (
|
|
55
|
+
lines: ReadonlyArray<Line>,
|
|
56
|
+
from: Numbers,
|
|
57
|
+
): readonly [Numbers, ReadonlyArray<DiffRow>] =>
|
|
58
|
+
Array.mapAccum(lines, from, (numbers, { kind, text }) => numberLine(numbers, kind, text));
|
|
59
|
+
|
|
52
60
|
/** The line diff of two texts (Myers, through jsdiff), each line numbered on its side. A
|
|
53
61
|
* missing final newline is not a change. */
|
|
54
62
|
export const diffRowsOf = (before: string, after: string): ReadonlyArray<DiffRow> =>
|
|
@@ -60,21 +68,16 @@ export const diffRowsOf = (before: string, after: string): ReadonlyArray<DiffRow
|
|
|
60
68
|
text,
|
|
61
69
|
})),
|
|
62
70
|
),
|
|
63
|
-
(lines) =>
|
|
64
|
-
Array.mapAccum(lines, { old: 1, new: 1 }, (numbers, { kind, text }) =>
|
|
65
|
-
numberLine(numbers, kind, text),
|
|
66
|
-
),
|
|
67
|
-
([, rows]) => rows,
|
|
71
|
+
(lines) => numbered(lines, { old: 1, new: 1 })[1],
|
|
68
72
|
);
|
|
69
73
|
|
|
74
|
+
const isChange = (row: DiffRow): boolean => row._tag === "Added" || row._tag === "Removed";
|
|
75
|
+
|
|
70
76
|
/** Whether a line was added or removed. */
|
|
71
|
-
export const hasChanges = (rows: ReadonlyArray<DiffRow>): boolean =>
|
|
72
|
-
Array.some(rows, (row) => row._tag === "Added" || row._tag === "Removed");
|
|
77
|
+
export const hasChanges = (rows: ReadonlyArray<DiffRow>): boolean => Array.some(rows, isChange);
|
|
73
78
|
|
|
74
79
|
// PATCH
|
|
75
80
|
|
|
76
|
-
type PatchState = Readonly<{ rows: ReadonlyArray<DiffRow>; numbers: Numbers }>;
|
|
77
|
-
|
|
78
81
|
const LINE_KIND_BY_PREFIX: Readonly<Record<string, LineKind>> = {
|
|
79
82
|
" ": "Same",
|
|
80
83
|
"+": "Added",
|
|
@@ -86,23 +89,27 @@ const LINE_KIND_BY_PREFIX: Readonly<Record<string, LineKind>> = {
|
|
|
86
89
|
const kindOf = (line: string): Option.Option<LineKind> =>
|
|
87
90
|
line === "" ? Option.some("Same") : Option.fromUndefinedOr(LINE_KIND_BY_PREFIX[line.charAt(0)]);
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{ rows: [...rows, ...gapRows], numbers: { old: hunk.oldStart, new: hunk.newStart } },
|
|
96
|
-
(state: PatchState, line): PatchState =>
|
|
97
|
-
// "" starts with a "\", a prefix that draws no row
|
|
98
|
-
Option.match(kindOf(line), {
|
|
99
|
-
onNone: () => state,
|
|
100
|
-
onSome: (kind) => {
|
|
101
|
-
const [nextNumbers, row] = numberLine(state.numbers, kind, line.slice(1));
|
|
102
|
-
return { rows: [...state.rows, row], numbers: nextNumbers };
|
|
103
|
-
},
|
|
104
|
-
}),
|
|
92
|
+
// "" starts with a "\", a prefix that draws no row
|
|
93
|
+
const linesOf = (hunk: StructuredPatchHunk): ReadonlyArray<Line> =>
|
|
94
|
+
Array.getSomes(
|
|
95
|
+
Array.map(hunk.lines, (line) =>
|
|
96
|
+
Option.map(kindOf(line), (kind) => ({ kind, text: line.slice(1) })),
|
|
97
|
+
),
|
|
105
98
|
);
|
|
99
|
+
|
|
100
|
+
// each hunk is numbered from its own header; only the gap before it needs the old line the
|
|
101
|
+
// previous one ended on
|
|
102
|
+
const readHunk = (
|
|
103
|
+
nextOld: number,
|
|
104
|
+
hunk: StructuredPatchHunk,
|
|
105
|
+
): readonly [number, ReadonlyArray<DiffRow>] => {
|
|
106
|
+
const gap = hunk.oldStart - nextOld;
|
|
107
|
+
const [end, rows] = numbered(linesOf(hunk), { old: hunk.oldStart, new: hunk.newStart });
|
|
108
|
+
|
|
109
|
+
return [
|
|
110
|
+
end.old,
|
|
111
|
+
gap > 0 ? [DiffRow.Fold({ count: gap, firstOldNumber: nextOld }), ...rows] : rows,
|
|
112
|
+
];
|
|
106
113
|
};
|
|
107
114
|
|
|
108
115
|
const onlyHunksOf = <File extends Readonly<{ hunks: Array<StructuredPatchHunk> }>>(
|
|
@@ -116,35 +123,29 @@ const onlyHunksOf = <File extends Readonly<{ hunks: Array<StructuredPatchHunk> }
|
|
|
116
123
|
: Option.none(),
|
|
117
124
|
});
|
|
118
125
|
|
|
119
|
-
/** The
|
|
120
|
-
* lines between hunks folded into a count. `None` unless it is one file with a hunk: text that
|
|
126
|
+
/** The hunks of one file's unified diff. `None` unless it is one file with a hunk: text that
|
|
121
127
|
* is not a diff, a binary change and several files have no lines to show. */
|
|
128
|
+
export const hunksOfPatch = (
|
|
129
|
+
patch: string,
|
|
130
|
+
): Option.Option<Array.NonEmptyReadonlyArray<StructuredPatchHunk>> =>
|
|
131
|
+
Option.flatMap(Option.liftThrowable(parsePatch)(patch), onlyHunksOf);
|
|
132
|
+
|
|
133
|
+
/** The rows of one file's unified diff, numbered from its hunks' headers, with the unchanged
|
|
134
|
+
* lines between hunks folded into a count. `None` where `hunksOfPatch` is. */
|
|
122
135
|
export const diffRowsOfPatch = (patch: string): Option.Option<ReadonlyArray<DiffRow>> =>
|
|
123
|
-
|
|
124
|
-
Option.liftThrowable(parsePatch)(patch.replaceAll("\r\n", "\n")),
|
|
125
|
-
Option.flatMap(onlyHunksOf),
|
|
126
|
-
Option.map(
|
|
127
|
-
(hunks) => Array.reduce(hunks, { rows: [], numbers: { old: 1, new: 1 } }, readHunk).rows,
|
|
128
|
-
),
|
|
129
|
-
);
|
|
136
|
+
Option.map(hunksOfPatch(patch), (hunks) => Array.flatten(Array.mapAccum(hunks, 1, readHunk)[1]));
|
|
130
137
|
|
|
131
138
|
// FOLD
|
|
132
139
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
folded: ReadonlyArray<SameRow>;
|
|
136
|
-
}>;
|
|
140
|
+
/** A run of unchanged lines being folded: its first line and how many there are. */
|
|
141
|
+
type Run = Readonly<{ first: SameRow; count: number }>;
|
|
137
142
|
|
|
138
143
|
// a run of one stays a line: a row that says "1 unchanged line" takes the same room as the line
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
:
|
|
143
|
-
|
|
144
|
-
const flushFold = ({ rows, folded }: FoldState): ReadonlyArray<DiffRow> =>
|
|
145
|
-
Array.match(folded, {
|
|
146
|
-
onEmpty: () => rows,
|
|
147
|
-
onNonEmpty: (run) => [...rows, foldRow(run)],
|
|
144
|
+
const runRows = (maybeRun: Option.Option<Run>): ReadonlyArray<DiffRow> =>
|
|
145
|
+
Option.match(maybeRun, {
|
|
146
|
+
onNone: () => [],
|
|
147
|
+
onSome: ({ first, count }) =>
|
|
148
|
+
count === 1 ? [first] : [DiffRow.Fold({ count, firstOldNumber: first.oldNumber })],
|
|
148
149
|
});
|
|
149
150
|
|
|
150
151
|
/** The diff's rows: every change with `context` unchanged lines around it, and each longer
|
|
@@ -153,14 +154,29 @@ export const foldUnchanged = (
|
|
|
153
154
|
rows: ReadonlyArray<DiffRow>,
|
|
154
155
|
context: number,
|
|
155
156
|
): ReadonlyArray<DiffRow> => {
|
|
157
|
+
// the changes before each index, so the changes in any window are one subtraction
|
|
158
|
+
const changesBefore = Array.scan(rows, 0, (count, row) => count + (isChange(row) ? 1 : 0));
|
|
156
159
|
const isKept = (index: number): boolean =>
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
Array.getUnsafe(changesBefore, Math.min(rows.length, index + context + 1)) -
|
|
161
|
+
Array.getUnsafe(changesBefore, Math.max(0, index - context)) >
|
|
162
|
+
0;
|
|
163
|
+
|
|
164
|
+
const [maybeRun, kept] = Array.mapAccum(
|
|
165
|
+
rows,
|
|
166
|
+
Option.none<Run>(),
|
|
167
|
+
(maybeRun, row, index): readonly [Option.Option<Run>, ReadonlyArray<DiffRow>] =>
|
|
161
168
|
row._tag === "Same" && !isKept(index)
|
|
162
|
-
?
|
|
163
|
-
|
|
164
|
-
|
|
169
|
+
? [
|
|
170
|
+
Option.some(
|
|
171
|
+
Option.match(maybeRun, {
|
|
172
|
+
onNone: () => ({ first: row, count: 1 }),
|
|
173
|
+
onSome: (run) => ({ ...run, count: run.count + 1 }),
|
|
174
|
+
}),
|
|
175
|
+
),
|
|
176
|
+
[],
|
|
177
|
+
]
|
|
178
|
+
: [Option.none(), [...runRows(maybeRun), row]],
|
|
165
179
|
);
|
|
180
|
+
|
|
181
|
+
return [...Array.flatten(kept), ...runRows(maybeRun)];
|
|
166
182
|
};
|
package/src/lineDiff/patch.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Array, Option, String, pipe } from "effect";
|
|
2
2
|
|
|
3
|
-
import { type DiffRow, diffRowsOfPatch } from "./diff.ts";
|
|
3
|
+
import { type DiffRow, diffRowsOfPatch, hunksOfPatch } from "./diff.ts";
|
|
4
4
|
|
|
5
5
|
// QUOTED PATHS
|
|
6
6
|
|
|
@@ -114,9 +114,12 @@ const lineNote = (head: ReadonlyArray<string>, hasRows: boolean): Option.Option<
|
|
|
114
114
|
}
|
|
115
115
|
};
|
|
116
116
|
|
|
117
|
-
|
|
117
|
+
/** How a section's lines are read: `None` where it has none to show. */
|
|
118
|
+
type RowsOf = (section: string) => Option.Option<ReadonlyArray<DiffRow>>;
|
|
119
|
+
|
|
120
|
+
const readSection = (section: string, rowsOf: RowsOf): PatchReading => {
|
|
118
121
|
const head = headOf(section);
|
|
119
|
-
const maybeRows =
|
|
122
|
+
const maybeRows = rowsOf(section);
|
|
120
123
|
const maybeRenamedFrom = Option.map(valueAfter(head, "rename from "), unquoted);
|
|
121
124
|
const notes = [
|
|
122
125
|
...Option.toArray(Option.map(maybeRenamedFrom, (from) => `Renamed from ${from}.`)),
|
|
@@ -150,14 +153,16 @@ const KINDS: Readonly<Record<string, string>> = {
|
|
|
150
153
|
|
|
151
154
|
const kindOf = (mode: string): string => KINDS[mode] ?? `mode ${mode}`;
|
|
152
155
|
|
|
153
|
-
const sideTable = (section: string, side: string): ReadonlyArray<Table> =>
|
|
154
|
-
Option.toArray(
|
|
155
|
-
Option.map(diffRowsOfPatch(section), (rows) => ({ rows, maybeSide: Option.some(side) })),
|
|
156
|
-
);
|
|
156
|
+
const sideTable = (section: string, side: string, rowsOf: RowsOf): ReadonlyArray<Table> =>
|
|
157
|
+
Option.toArray(Option.map(rowsOf(section), (rows) => ({ rows, maybeSide: Option.some(side) })));
|
|
157
158
|
|
|
158
159
|
// a file that became a symlink (or any other kind) is two sections for one path: the old
|
|
159
160
|
// kind deleted, then the new one added
|
|
160
|
-
const readTypeChange = (
|
|
161
|
+
const readTypeChange = (
|
|
162
|
+
gone: string,
|
|
163
|
+
added: string,
|
|
164
|
+
rowsOf: RowsOf,
|
|
165
|
+
): Option.Option<PatchReading> => {
|
|
161
166
|
const [goneHead, addedHead] = [headOf(gone), headOf(added)];
|
|
162
167
|
|
|
163
168
|
return Option.flatMap(
|
|
@@ -170,14 +175,16 @@ const readTypeChange = (gone: string, added: string): Option.Option<PatchReading
|
|
|
170
175
|
? Option.some({
|
|
171
176
|
maybeRenamedFrom: Option.none(),
|
|
172
177
|
notes: [`It was ${kindOf(before)} and is now ${kindOf(after)}.`],
|
|
173
|
-
tables: [
|
|
178
|
+
tables: [
|
|
179
|
+
...sideTable(gone, "as it was", rowsOf),
|
|
180
|
+
...sideTable(added, "as it is", rowsOf),
|
|
181
|
+
],
|
|
174
182
|
})
|
|
175
183
|
: Option.none(),
|
|
176
184
|
);
|
|
177
185
|
};
|
|
178
186
|
|
|
179
|
-
|
|
180
|
-
export const readPatch = (patch: string): PatchReading => {
|
|
187
|
+
const readPatchWith = (patch: string, rowsOf: RowsOf): PatchReading => {
|
|
181
188
|
const text = patch.replaceAll("\r\n", "\n");
|
|
182
189
|
|
|
183
190
|
if (String.isEmpty(String.trim(text))) {
|
|
@@ -187,15 +194,18 @@ export const readPatch = (patch: string): PatchReading => {
|
|
|
187
194
|
const [first, second, ...rest] = sections;
|
|
188
195
|
|
|
189
196
|
if (first !== undefined && second === undefined) {
|
|
190
|
-
return readSection(first);
|
|
197
|
+
return readSection(first, rowsOf);
|
|
191
198
|
} else if (first !== undefined && second !== undefined && Array.isArrayEmpty(rest)) {
|
|
192
|
-
return Option.getOrElse(readTypeChange(first, second), () => unreadable);
|
|
199
|
+
return Option.getOrElse(readTypeChange(first, second, rowsOf), () => unreadable);
|
|
193
200
|
} else {
|
|
194
201
|
return unreadable;
|
|
195
202
|
}
|
|
196
203
|
}
|
|
197
204
|
};
|
|
198
205
|
|
|
206
|
+
/** What `patchView` makes of a patch: the notes it prints and the tables it draws. */
|
|
207
|
+
export const readPatch = (patch: string): PatchReading => readPatchWith(patch, diffRowsOfPatch);
|
|
208
|
+
|
|
199
209
|
// DESCRIPTION
|
|
200
210
|
|
|
201
211
|
/** What a patch says beyond its lines, as `patchView` prints it. */
|
|
@@ -212,7 +222,10 @@ export type PatchDescription = Readonly<{
|
|
|
212
222
|
* prints (`["Renamed from old.txt."]`), for a caller that prints them itself, and the path a
|
|
213
223
|
* rename left. */
|
|
214
224
|
export const describePatch = (patch: string): PatchDescription => {
|
|
215
|
-
|
|
225
|
+
// the notes need only whether a section has lines, so none are numbered
|
|
226
|
+
const { maybeRenamedFrom, notes } = readPatchWith(patch, (section) =>
|
|
227
|
+
Option.as(hunksOfPatch(section), []),
|
|
228
|
+
);
|
|
216
229
|
|
|
217
230
|
return { maybeRenamedFrom, notes };
|
|
218
231
|
};
|
|
@@ -27,13 +27,6 @@ export type ViewConfig = Readonly<{
|
|
|
27
27
|
label?: string;
|
|
28
28
|
}>;
|
|
29
29
|
|
|
30
|
-
const STATE_CLASS: Readonly<Record<State, string>> = {
|
|
31
|
-
Online: "",
|
|
32
|
-
Connecting: " connecting",
|
|
33
|
-
Reconnecting: " reconnecting",
|
|
34
|
-
Offline: " offline",
|
|
35
|
-
};
|
|
36
|
-
|
|
37
30
|
/** The connection word, in a top bar or a status bar. It is a status (`role="status"`), so a
|
|
38
31
|
* change of state is read out; while it connects it prints the working cursor. */
|
|
39
32
|
export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html => {
|
|
@@ -41,7 +34,7 @@ export const view = <Message>(config: ViewConfig, h: HtmlBuilder<Message>): Html
|
|
|
41
34
|
const isWorking = state === "Connecting" || state === "Reconnecting";
|
|
42
35
|
|
|
43
36
|
return h.span(
|
|
44
|
-
[h.Role("status"), h.Class(`link-state${
|
|
37
|
+
[h.Role("status"), h.Class(`link-state${state === "Online" ? "" : ` ${stateWord(state)}`}`)],
|
|
45
38
|
[label, ...(isWorking ? [h.span([h.Class("cursor"), h.AriaHidden(true)])] : [])],
|
|
46
39
|
);
|
|
47
40
|
};
|
package/src/menu/slip.ts
CHANGED
|
@@ -58,8 +58,8 @@ const edgeAttributes = <Message>(
|
|
|
58
58
|
id: string,
|
|
59
59
|
h: HtmlBuilder<Message>,
|
|
60
60
|
) => [
|
|
61
|
-
...(Option.contains(context.firstId, id) ? [h.
|
|
62
|
-
...(Option.contains(context.lastId, id) ? [h.
|
|
61
|
+
...(Option.contains(context.firstId, id) ? [h.Attribute(FIRST_ATTRIBUTE, "")] : []),
|
|
62
|
+
...(Option.contains(context.lastId, id) ? [h.Attribute(LAST_ATTRIBUTE, "")] : []),
|
|
63
63
|
];
|
|
64
64
|
|
|
65
65
|
const wordsView = <Message>(
|
package/src/meter/meter.ts
CHANGED
|
@@ -105,32 +105,41 @@ const numbersText = (value: number, max: number, unit: string | undefined): stri
|
|
|
105
105
|
return unit === undefined ? numbers : `${numbers} ${unit}`;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
|
+
/** How high a reading is: full at `max`, high from `HIGH_SHARE` of it, calm below. The
|
|
109
|
+
* percentage and a toned meter's cells both print it. */
|
|
110
|
+
type Level = "Full" | "High" | "Calm";
|
|
111
|
+
|
|
112
|
+
const levelOf = (value: number, max: number): Level => {
|
|
113
|
+
if (value >= max) {
|
|
114
|
+
return "Full";
|
|
115
|
+
} else if (value >= max * HIGH_SHARE) {
|
|
116
|
+
return "High";
|
|
117
|
+
} else {
|
|
118
|
+
return "Calm";
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
|
|
108
122
|
const percentView = <Message>(
|
|
109
123
|
config: MeterConfig,
|
|
110
124
|
percent: number,
|
|
111
|
-
|
|
125
|
+
level: Level,
|
|
112
126
|
h: HtmlBuilder<Message>,
|
|
113
127
|
): Html => {
|
|
114
128
|
const percentText = `${percent}%`;
|
|
115
|
-
const high = config.max * HIGH_SHARE;
|
|
116
129
|
|
|
117
|
-
if (
|
|
130
|
+
if (level === "Full") {
|
|
118
131
|
return Chip.view({ label: config.full ?? percentText, tone: "Blocked" }, h);
|
|
119
|
-
} else if (
|
|
132
|
+
} else if (level === "High") {
|
|
120
133
|
return Mark.readingView({ label: percentText }, h);
|
|
121
134
|
} else {
|
|
122
135
|
return h.span([h.Class("pct")], [percentText]);
|
|
123
136
|
}
|
|
124
137
|
};
|
|
125
138
|
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return " toned high";
|
|
131
|
-
} else {
|
|
132
|
-
return " toned";
|
|
133
|
-
}
|
|
139
|
+
const TONE_CLASS: Readonly<Record<Level, string>> = {
|
|
140
|
+
Full: " toned full",
|
|
141
|
+
High: " toned high",
|
|
142
|
+
Calm: " toned",
|
|
134
143
|
};
|
|
135
144
|
|
|
136
145
|
const meterView = <Message>(config: MeterConfig, h: HtmlBuilder<Message>): Html => {
|
|
@@ -146,9 +155,10 @@ const meterView = <Message>(config: MeterConfig, h: HtmlBuilder<Message>): Html
|
|
|
146
155
|
isToned = false,
|
|
147
156
|
} = config;
|
|
148
157
|
const share = max > 0 ? Math.min(1, Math.max(0, value / max)) : 0;
|
|
149
|
-
const
|
|
158
|
+
const level = levelOf(value, max);
|
|
159
|
+
const isFull = level === "Full";
|
|
150
160
|
const numbers = config.valueText ?? numbersText(value, max, config.unit);
|
|
151
|
-
const rowClass = `meter-row${isStale ? " stale" : ""}${isToned ?
|
|
161
|
+
const rowClass = `meter-row${isStale ? " stale" : ""}${isToned ? TONE_CLASS[level] : ""}`;
|
|
152
162
|
|
|
153
163
|
return UiMeter.view(
|
|
154
164
|
{
|
|
@@ -179,7 +189,7 @@ const meterView = <Message>(config: MeterConfig, h: HtmlBuilder<Message>): Html
|
|
|
179
189
|
[...groups.meter, h.Class("cells")],
|
|
180
190
|
cellRowView(METER_CELLS, scaled(share, METER_CELLS, isFull), h),
|
|
181
191
|
),
|
|
182
|
-
percentView(config, scaled(share, PERCENT, isFull),
|
|
192
|
+
percentView(config, scaled(share, PERCENT, isFull), level, h),
|
|
183
193
|
...(hasNumbers ? [h.span([h.Class("meter-num tnum")], [numbers])] : []),
|
|
184
194
|
],
|
|
185
195
|
),
|
package/src/notice/notice.ts
CHANGED
|
@@ -22,7 +22,8 @@ const toneGlyph = (tone: Tone): GlyphName =>
|
|
|
22
22
|
Match.exhaustive,
|
|
23
23
|
);
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
/** The class a tone adds to a notice's, a toast's or a glyph box's own. */
|
|
26
|
+
export const TONE_CLASS: Readonly<Record<Tone, string>> = {
|
|
26
27
|
Warn: "",
|
|
27
28
|
Error: " error",
|
|
28
29
|
Ok: " ok",
|