@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.
Files changed (176) hide show
  1. package/DESIGN.md +18 -17
  2. package/dist/src/adapters/mermaid.d.ts.map +1 -1
  3. package/dist/src/adapters/mermaid.js +0 -24
  4. package/dist/src/announcer/announcer.d.ts.map +1 -1
  5. package/dist/src/announcer/announcer.js +12 -12
  6. package/dist/src/board/board.d.ts +0 -10
  7. package/dist/src/board/board.d.ts.map +1 -1
  8. package/dist/src/board/board.js +36 -64
  9. package/dist/src/board/index.d.ts +2 -1
  10. package/dist/src/board/index.d.ts.map +1 -1
  11. package/dist/src/board/index.js +2 -1
  12. package/dist/src/button/button.d.ts +1 -1
  13. package/dist/src/button/button.d.ts.map +1 -1
  14. package/dist/src/commandPalette/commandPalette.d.ts.map +1 -1
  15. package/dist/src/commandPalette/commandPalette.js +78 -83
  16. package/dist/src/commandPalette/keys.d.ts +2 -2
  17. package/dist/src/commandPalette/keys.d.ts.map +1 -1
  18. package/dist/src/commandPalette/keys.js +2 -4
  19. package/dist/src/commandPalette/match.d.ts.map +1 -1
  20. package/dist/src/commandPalette/match.js +2 -2
  21. package/dist/src/confirmDialog/confirmDialog.d.ts.map +1 -1
  22. package/dist/src/confirmDialog/confirmDialog.js +1 -2
  23. package/dist/src/copyButton/copyButton.d.ts +0 -5
  24. package/dist/src/copyButton/copyButton.d.ts.map +1 -1
  25. package/dist/src/copyButton/copyButton.js +2 -2
  26. package/dist/src/crumbs/crumbs.d.ts +0 -2
  27. package/dist/src/crumbs/crumbs.d.ts.map +1 -1
  28. package/dist/src/crumbs/crumbs.js +2 -2
  29. package/dist/src/dialog/dialog.d.ts +2 -0
  30. package/dist/src/dialog/dialog.d.ts.map +1 -1
  31. package/dist/src/dialog/dialog.js +3 -2
  32. package/dist/src/drag/drag.d.ts +28 -0
  33. package/dist/src/drag/drag.d.ts.map +1 -1
  34. package/dist/src/drag/drag.js +39 -17
  35. package/dist/src/editableTitle/editableTitle.d.ts +4 -4
  36. package/dist/src/field/field.d.ts +7 -1
  37. package/dist/src/field/field.d.ts.map +1 -1
  38. package/dist/src/field/field.js +14 -6
  39. package/dist/src/fieldset/fieldset.d.ts.map +1 -1
  40. package/dist/src/fieldset/fieldset.js +2 -4
  41. package/dist/src/fileTree/fileTree.d.ts.map +1 -1
  42. package/dist/src/fileTree/fileTree.js +16 -16
  43. package/dist/src/iconButton/iconButton.d.ts +6 -1
  44. package/dist/src/iconButton/iconButton.d.ts.map +1 -1
  45. package/dist/src/iconButton/iconButton.js +7 -2
  46. package/dist/src/inlineInput/inlineInput.d.ts +4 -0
  47. package/dist/src/inlineInput/inlineInput.d.ts.map +1 -1
  48. package/dist/src/inlineInput/inlineInput.js +9 -6
  49. package/dist/src/keyHelp/index.d.ts +1 -1
  50. package/dist/src/keyHelp/index.d.ts.map +1 -1
  51. package/dist/src/keyHelp/index.js +1 -1
  52. package/dist/src/keyHelp/keyHelp.d.ts +1 -3
  53. package/dist/src/keyHelp/keyHelp.d.ts.map +1 -1
  54. package/dist/src/keyHelp/keyHelp.js +3 -6
  55. package/dist/src/keyHint/keyLabel.d.ts +2 -0
  56. package/dist/src/keyHint/keyLabel.d.ts.map +1 -1
  57. package/dist/src/keyHint/keyLabel.js +3 -2
  58. package/dist/src/lineDiff/diff.d.ts +6 -3
  59. package/dist/src/lineDiff/diff.d.ts.map +1 -1
  60. package/dist/src/lineDiff/diff.js +40 -28
  61. package/dist/src/lineDiff/patch.d.ts.map +1 -1
  62. package/dist/src/lineDiff/patch.js +18 -13
  63. package/dist/src/linkState/linkState.d.ts.map +1 -1
  64. package/dist/src/linkState/linkState.js +1 -7
  65. package/dist/src/menu/slip.js +2 -2
  66. package/dist/src/meter/meter.d.ts.map +1 -1
  67. package/dist/src/meter/meter.js +22 -17
  68. package/dist/src/notice/notice.d.ts +2 -0
  69. package/dist/src/notice/notice.d.ts.map +1 -1
  70. package/dist/src/notice/notice.js +2 -1
  71. package/dist/src/panel/panel.d.ts +3 -0
  72. package/dist/src/panel/panel.d.ts.map +1 -1
  73. package/dist/src/panel/panel.js +2 -1
  74. package/dist/src/passwordField/passwordField.d.ts.map +1 -1
  75. package/dist/src/passwordField/passwordField.js +3 -7
  76. package/dist/src/rail/index.d.ts +1 -1
  77. package/dist/src/rail/index.d.ts.map +1 -1
  78. package/dist/src/rail/index.js +1 -1
  79. package/dist/src/rail/rail.d.ts +95 -20
  80. package/dist/src/rail/rail.d.ts.map +1 -1
  81. package/dist/src/rail/rail.js +103 -46
  82. package/dist/src/reel/layout.d.ts +9 -5
  83. package/dist/src/reel/layout.d.ts.map +1 -1
  84. package/dist/src/reel/layout.js +11 -5
  85. package/dist/src/reel/reel.d.ts.map +1 -1
  86. package/dist/src/reel/reel.js +122 -68
  87. package/dist/src/scheme/scheme.d.ts +1 -1
  88. package/dist/src/scheme/scheme.d.ts.map +1 -1
  89. package/dist/src/segmented/segmented.d.ts +0 -3
  90. package/dist/src/segmented/segmented.d.ts.map +1 -1
  91. package/dist/src/segmented/segmented.js +1 -6
  92. package/dist/src/selectField/selectField.d.ts.map +1 -1
  93. package/dist/src/selectField/selectField.js +3 -10
  94. package/dist/src/sortable/index.d.ts +1 -1
  95. package/dist/src/sortable/index.d.ts.map +1 -1
  96. package/dist/src/sortable/index.js +1 -1
  97. package/dist/src/sortable/sortable.d.ts +39 -40
  98. package/dist/src/sortable/sortable.d.ts.map +1 -1
  99. package/dist/src/sortable/sortable.js +14 -31
  100. package/dist/src/stack/stack.d.ts +1 -1
  101. package/dist/src/stack/stack.d.ts.map +1 -1
  102. package/dist/src/stack/stack.js +1 -1
  103. package/dist/src/tabs/tabs.d.ts +1 -1
  104. package/dist/src/tabs/tabs.d.ts.map +1 -1
  105. package/dist/src/tabs/tabs.js +1 -1
  106. package/dist/src/textareaField/textareaField.d.ts.map +1 -1
  107. package/dist/src/textareaField/textareaField.js +3 -7
  108. package/dist/src/time/time.d.ts.map +1 -1
  109. package/dist/src/time/time.js +4 -2
  110. package/dist/src/tips/index.d.ts +1 -1
  111. package/dist/src/tips/index.d.ts.map +1 -1
  112. package/dist/src/tips/tips.d.ts +30 -1
  113. package/dist/src/tips/tips.d.ts.map +1 -1
  114. package/dist/src/tips/tips.js +53 -31
  115. package/dist/src/toast/index.d.ts +1 -1
  116. package/dist/src/toast/index.d.ts.map +1 -1
  117. package/dist/src/toast/toast.d.ts +5 -5
  118. package/dist/src/toast/toast.d.ts.map +1 -1
  119. package/dist/src/toast/toast.js +1 -6
  120. package/dist/src/toolbar/toolbar.d.ts +0 -3
  121. package/dist/src/toolbar/toolbar.d.ts.map +1 -1
  122. package/dist/src/toolbar/toolbar.js +1 -2
  123. package/package.json +1 -1
  124. package/src/adapters/mermaid.ts +0 -24
  125. package/src/announcer/announcer.ts +13 -17
  126. package/src/board/board.ts +32 -80
  127. package/src/board/index.ts +1 -1
  128. package/src/button/button.ts +1 -1
  129. package/src/commandPalette/commandPalette.ts +21 -38
  130. package/src/commandPalette/keys.ts +2 -4
  131. package/src/commandPalette/match.ts +3 -3
  132. package/src/confirmDialog/confirmDialog.ts +5 -3
  133. package/src/copyButton/copyButton.ts +2 -5
  134. package/src/crumbs/crumbs.ts +2 -4
  135. package/src/dialog/dialog.ts +3 -2
  136. package/src/drag/drag.ts +54 -5
  137. package/src/field/field.ts +29 -7
  138. package/src/fieldset/fieldset.ts +2 -4
  139. package/src/fileTree/fileTree.ts +10 -12
  140. package/src/iconButton/iconButton.ts +19 -2
  141. package/src/inlineInput/inlineInput.ts +11 -2
  142. package/src/keyHelp/index.ts +0 -1
  143. package/src/keyHelp/keyHelp.ts +3 -9
  144. package/src/keyHint/keyLabel.ts +5 -2
  145. package/src/lineDiff/diff.ts +70 -54
  146. package/src/lineDiff/patch.ts +27 -14
  147. package/src/linkState/linkState.ts +1 -8
  148. package/src/menu/slip.ts +2 -2
  149. package/src/meter/meter.ts +25 -15
  150. package/src/notice/notice.ts +2 -1
  151. package/src/panel/panel.ts +5 -0
  152. package/src/passwordField/passwordField.ts +3 -7
  153. package/src/pixel-paper.css +200 -134
  154. package/src/rail/index.ts +2 -0
  155. package/src/rail/rail.ts +124 -44
  156. package/src/reel/layout.ts +14 -7
  157. package/src/reel/reel.ts +132 -63
  158. package/src/scheme/scheme.ts +1 -1
  159. package/src/segmented/segmented.ts +1 -9
  160. package/src/selectField/selectField.ts +3 -10
  161. package/src/sortable/index.ts +0 -1
  162. package/src/sortable/sortable.ts +35 -54
  163. package/src/stack/stack.ts +1 -1
  164. package/src/tabs/tabs.ts +1 -1
  165. package/src/textareaField/textareaField.ts +3 -7
  166. package/src/time/time.ts +6 -7
  167. package/src/tips/index.ts +1 -1
  168. package/src/tips/tips.ts +71 -35
  169. package/src/toast/index.ts +1 -1
  170. package/src/toast/toast.ts +2 -8
  171. package/src/tokens.css +7 -0
  172. package/src/toolbar/toolbar.ts +0 -5
  173. package/dist/src/inlineInput/inline.d.ts +0 -6
  174. package/dist/src/inlineInput/inline.d.ts.map +0 -1
  175. package/dist/src/inlineInput/inline.js +0 -7
  176. package/src/inlineInput/inline.ts +0 -13
package/src/tips/tips.ts CHANGED
@@ -1,4 +1,15 @@
1
- import { Array, Duration, Effect, Match, Number, Option, Schema, Stream, pipe } from "effect";
1
+ import {
2
+ Array,
3
+ Duration,
4
+ Effect,
5
+ Equal,
6
+ Match,
7
+ Number,
8
+ Option,
9
+ Schema,
10
+ Stream,
11
+ pipe,
12
+ } from "effect";
2
13
  import * as Command from "foldkit/command";
3
14
  import * as Dom from "foldkit/dom";
4
15
  import type { Html } from "foldkit/html";
@@ -23,7 +34,10 @@ type Side = typeof Side.Type;
23
34
  export const Trigger = Schema.Literals(["Pointer", "Focus"]);
24
35
  export type Trigger = typeof Trigger.Type;
25
36
 
26
- const Words = { text: Schema.String, side: Side };
37
+ /** What a tip says: its control's name, and the key that does the same where the control
38
+ * names one (`data-tip-key`). */
39
+ const Words = { text: Schema.String, maybeKey: Schema.Option(Schema.String), side: Side };
40
+ type Words = Readonly<{ text: string; maybeKey: Option.Option<string>; side: Side }>;
27
41
  const ShownWords = { ...Words, trigger: Trigger };
28
42
 
29
43
  /** Where the page's tip is. `Cold`: none lately. `Waiting`: a pointer rests on a control, for
@@ -51,10 +65,10 @@ export type Model = typeof Model.Type;
51
65
 
52
66
  /** Union of all Messages the tip layer can produce. */
53
67
  export const Message = defineMessageUnion({
54
- EnteredTipTarget: { text: Schema.String, side: Side },
68
+ EnteredTipTarget: Words,
55
69
  LeftTipTarget: {},
56
70
  EnteredTip: {},
57
- FocusedTipTarget: { text: Schema.String, side: Side },
71
+ FocusedTipTarget: Words,
58
72
  BlurredTipTarget: {},
59
73
  PressedPointer: {},
60
74
  PressedKey: {},
@@ -103,6 +117,9 @@ const WARMTH = Duration.millis(400);
103
117
  /** The attribute that holds a control's tip: its accessible name. */
104
118
  const TIP_ATTRIBUTE = "data-tip";
105
119
 
120
+ /** The attribute that holds the key a control's tip prints after its name. */
121
+ const TIP_KEY_ATTRIBUTE = "data-tip-key";
122
+
106
123
  const TIP_SELECTOR = `[${TIP_ATTRIBUTE}]`;
107
124
 
108
125
  const hasText = (element: HTMLElement, text: string): boolean =>
@@ -204,9 +221,11 @@ export const WaitBeforeCooling = Command.define("WaitBeforeCooling", {
204
221
 
205
222
  type UpdateReturn = Update.Return<Model, Message>;
206
223
 
207
- const show = (model: Model, text: string, side: Side, trigger: Trigger): UpdateReturn => ({
208
- model: modifyFields(model, { state: () => TipState.Shown({ text, side, trigger }) }),
209
- commands: [ShowTip({ tipId: model.id, text, trigger })],
224
+ const wordsOf = ({ text, maybeKey, side }: Words): Words => ({ text, maybeKey, side });
225
+
226
+ const show = (model: Model, words: Words, trigger: Trigger): UpdateReturn => ({
227
+ model: modifyFields(model, { state: () => TipState.Shown({ ...wordsOf(words), trigger }) }),
228
+ commands: [ShowTip({ tipId: model.id, text: words.text, trigger })],
210
229
  });
211
230
 
212
231
  const hide = (model: Model): UpdateReturn => {
@@ -217,12 +236,12 @@ const hide = (model: Model): UpdateReturn => {
217
236
  };
218
237
  };
219
238
 
220
- const wait = (model: Model, text: string, side: Side): UpdateReturn => {
239
+ const wait = (model: Model, words: Words): UpdateReturn => {
221
240
  const nextVersion = Number.increment(model.version);
222
241
  return {
223
242
  model: modifyFields(model, {
224
243
  version: () => nextVersion,
225
- state: () => TipState.Waiting({ text, side }),
244
+ state: () => TipState.Waiting(wordsOf(words)),
226
245
  }),
227
246
  commands: [WaitBeforeShowing({ version: nextVersion })],
228
247
  };
@@ -233,7 +252,7 @@ const leave = (model: Model, shown: typeof TipState.Shown.Type): UpdateReturn =>
233
252
  return {
234
253
  model: modifyFields(model, {
235
254
  version: () => nextVersion,
236
- state: () => TipState.Leaving({ text: shown.text, side: shown.side, trigger: shown.trigger }),
255
+ state: () => TipState.Leaving({ ...wordsOf(shown), trigger: shown.trigger }),
237
256
  }),
238
257
  commands: [WaitBeforeHiding({ version: nextVersion })],
239
258
  };
@@ -253,26 +272,26 @@ const dismiss = (model: Model): UpdateReturn =>
253
272
  Warm: () => ({ model }),
254
273
  });
255
274
 
256
- const entered = (model: Model, text: string, side: Side): UpdateReturn =>
275
+ const entered = (model: Model, words: Words): UpdateReturn =>
257
276
  TipState.match<UpdateReturn>(model.state, {
258
- Cold: () => wait(model, text, side),
259
- Waiting: (waiting) => (waiting.text === text ? { model } : wait(model, text, side)),
260
- Shown: (shown) => (shown.text === text ? { model } : show(model, text, side, "Pointer")),
277
+ Cold: () => wait(model, words),
278
+ Waiting: (waiting) => (waiting.text === words.text ? { model } : wait(model, words)),
279
+ Shown: (shown) => (shown.text === words.text ? { model } : show(model, words, "Pointer")),
261
280
  Leaving: (leaving) =>
262
- leaving.text === text
281
+ leaving.text === words.text
263
282
  ? {
264
283
  model: modifyFields(model, {
265
- state: () => TipState.Shown({ text, side, trigger: leaving.trigger }),
284
+ state: () => TipState.Shown({ ...wordsOf(words), trigger: leaving.trigger }),
266
285
  }),
267
286
  }
268
- : show(model, text, side, "Pointer"),
269
- Warm: () => show(model, text, side, "Pointer"),
287
+ : show(model, words, "Pointer"),
288
+ Warm: () => show(model, words, "Pointer"),
270
289
  });
271
290
 
272
291
  /** Processes a tip layer Message and returns the next Model and Commands. */
273
292
  export const update = (model: Model, message: Message) =>
274
293
  Message.match<UpdateReturn>(message, {
275
- EnteredTipTarget: ({ text, side }) => entered(model, text, side),
294
+ EnteredTipTarget: (words) => entered(model, wordsOf(words)),
276
295
  LeftTipTarget: () =>
277
296
  TipState.matchOrElse<UpdateReturn>(
278
297
  model.state,
@@ -286,13 +305,15 @@ export const update = (model: Model, message: Message) =>
286
305
  TipState.matchOrElse<UpdateReturn>(
287
306
  model.state,
288
307
  {
289
- Leaving: ({ text, side, trigger }) => ({
290
- model: modifyFields(model, { state: () => TipState.Shown({ text, side, trigger }) }),
308
+ Leaving: (leaving) => ({
309
+ model: modifyFields(model, {
310
+ state: () => TipState.Shown({ ...wordsOf(leaving), trigger: leaving.trigger }),
311
+ }),
291
312
  }),
292
313
  },
293
314
  () => ({ model }),
294
315
  ),
295
- FocusedTipTarget: ({ text, side }) => show(model, text, side, "Focus"),
316
+ FocusedTipTarget: (words) => show(model, wordsOf(words), "Focus"),
296
317
  BlurredTipTarget: () => dismiss(model),
297
318
  PressedPointer: () => dismiss(model),
298
319
  PressedKey: () => dismiss(model),
@@ -300,8 +321,8 @@ export const update = (model: Model, message: Message) =>
300
321
  TipState.matchOrElse<UpdateReturn>(
301
322
  model.state,
302
323
  {
303
- Waiting: ({ text, side }) =>
304
- model.version === version ? show(model, text, side, "Pointer") : { model },
324
+ Waiting: (waiting) =>
325
+ model.version === version ? show(model, wordsOf(waiting), "Pointer") : { model },
305
326
  },
306
327
  () => ({ model }),
307
328
  ),
@@ -328,10 +349,15 @@ type TargetMessage =
328
349
  | typeof Message.LeftTipTarget.Type
329
350
  | typeof Message.EnteredTip.Type;
330
351
 
331
- type TipTarget = Readonly<{ element: HTMLElement; text: string; side: Side }>;
352
+ type TipTarget = Words & Readonly<{ element: HTMLElement }>;
332
353
 
354
+ /** A control in a vertical strip of tools, or set in vertical writing (a folded rail's title
355
+ * bar), takes its tip at its side, where the tip would cover the next tool or the top bar. */
333
356
  const sideOf = (element: Element): Side =>
334
- element.closest('[aria-orientation="vertical"]') === null ? "Top" : "Right";
357
+ element.closest('[aria-orientation="vertical"]') === null &&
358
+ !getComputedStyle(element).writingMode.startsWith("vertical")
359
+ ? "Top"
360
+ : "Right";
335
361
 
336
362
  const tipTargetOf = (target: EventTarget | null): Option.Option<TipTarget> =>
337
363
  pipe(
@@ -341,6 +367,7 @@ const tipTargetOf = (target: EventTarget | null): Option.Option<TipTarget> =>
341
367
  Option.map(Option.fromNullishOr(element.getAttribute(TIP_ATTRIBUTE)), (text) => ({
342
368
  element,
343
369
  text,
370
+ maybeKey: Option.fromNullishOr(element.getAttribute(TIP_KEY_ATTRIBUTE)),
344
371
  side: sideOf(element),
345
372
  })),
346
373
  ),
@@ -366,7 +393,7 @@ export const pointerOverMessage = (
366
393
  return Option.some(
367
394
  Option.match(tipTargetOf(event.target), {
368
395
  onNone: () => Message.LeftTipTarget(),
369
- onSome: ({ text, side }) => Message.EnteredTipTarget({ text, side }),
396
+ onSome: (target) => Message.EnteredTipTarget(wordsOf(target)),
370
397
  }),
371
398
  );
372
399
  };
@@ -376,7 +403,7 @@ export const pointerOverMessage = (
376
403
  const focusInMessage = (event: FocusEvent): Option.Option<Message> =>
377
404
  Option.map(
378
405
  Option.filter(tipTargetOf(event.target), ({ element }) => element.matches(":focus-visible")),
379
- ({ text, side }) => Message.FocusedTipTarget({ text, side }),
406
+ (target) => Message.FocusedTipTarget(wordsOf(target)),
380
407
  );
381
408
 
382
409
  /** Focus leaving a control with a tip, not into a part of itself, closes the tip (and
@@ -421,7 +448,9 @@ const pointerOverChanges = (tipId: string): Stream.Stream<Message> =>
421
448
  const sameWords = (previous: Message, next: Message): boolean =>
422
449
  previous._tag !== "EnteredTipTarget" ||
423
450
  next._tag !== "EnteredTipTarget" ||
424
- (previous.text === next.text && previous.side === next.side);
451
+ (previous.text === next.text &&
452
+ previous.side === next.side &&
453
+ Equal.equals(previous.maybeKey, next.maybeKey));
425
454
 
426
455
  const tipSubscriptions = Subscription.make<Model, Message>()((entry) => ({
427
456
  pointerAndFocus: entry(
@@ -480,16 +509,17 @@ export const subscriptions = <Prefix extends string>(keyPrefix: Prefix) =>
480
509
 
481
510
  // VIEW
482
511
 
483
- const wordsOf = (model: Model): Option.Option<Readonly<{ text: string; side: Side }>> =>
512
+ const maybeWordsOf = (model: Model): Option.Option<Words> =>
484
513
  Match.value(model.state).pipe(
485
- Match.withReturnType<Option.Option<Readonly<{ text: string; side: Side }>>>(),
486
- Match.tag("Waiting", "Shown", "Leaving", ({ text, side }) => Option.some({ text, side })),
514
+ Match.withReturnType<Option.Option<Words>>(),
515
+ Match.tag("Waiting", "Shown", "Leaving", (words) => Option.some(wordsOf(words))),
487
516
  Match.orElse(() => Option.none()),
488
517
  );
489
518
 
490
519
  /**
491
520
  * The page's one tip: `div.tip[popover="manual"][aria-hidden="true"]`, which shows the words
492
- * of any control carrying `data-tip` (IconButton, the glyph switch, a board's grip). Render it
521
+ * of any control carrying `data-tip` (IconButton, the glyph switch, a board's grip), then the
522
+ * key in its `data-tip-key` where it has one (an IconButton's `keyHint`). Render it
493
523
  * once, beside the Announcer, and lift `subscriptions(keyPrefix)`. It opens on a resting
494
524
  * pointer after a moment (at once while another has just closed), never on touch, and on
495
525
  * keyboard focus; it can be hovered; leaving, blur, a press or any key closes it, and Escape
@@ -501,7 +531,7 @@ const wordsOf = (model: Model): Option.Option<Readonly<{ text: string; side: Sid
501
531
  * accessibility tree, and any control takes one by carrying `data-tip`.
502
532
  */
503
533
  export const view = defineView<Model, Message>((model, h): Html => {
504
- const maybeWords = wordsOf(model);
534
+ const maybeWords = maybeWordsOf(model);
505
535
 
506
536
  return h.div(
507
537
  [
@@ -513,6 +543,12 @@ export const view = defineView<Model, Message>((model, h): Html => {
513
543
  ? [h.DataAttribute("side", "right")]
514
544
  : []),
515
545
  ],
516
- Option.isSome(maybeWords) ? [maybeWords.value.text] : [],
546
+ Option.match(maybeWords, {
547
+ onNone: () => [],
548
+ onSome: ({ text, maybeKey }) => [
549
+ text,
550
+ ...Array.fromOption(Option.map(maybeKey, (key) => h.kbd([h.Class("key-hint")], [key]))),
551
+ ],
552
+ }),
517
553
  );
518
554
  });
@@ -13,6 +13,6 @@ export {
13
13
  update,
14
14
  view,
15
15
  } from "./toast.ts";
16
- export type { InitConfig, ShowConfig, ShowReturn } from "./toast.ts";
16
+ export type { InitConfig, ShowConfig } from "./toast.ts";
17
17
  export type { Action, Tone } from "./stack.ts";
18
18
  export { Focus } from "../focus/focus.ts";
@@ -295,7 +295,7 @@ const oldestCalm = (model: Model): Option.Option<Stack.Entry> => {
295
295
 
296
296
  /** What `show` returns: the next Model and Commands, and the new slip's id, to take it back
297
297
  * with `dismiss`. */
298
- export type ShowReturn = Update.Return<Model, Message> & Readonly<{ entryId: string }>;
298
+ type ShowReturn = Update.Return<Model, Message> & Readonly<{ entryId: string }>;
299
299
 
300
300
  const spoken = (config: ShowConfig): string =>
301
301
  config.body === undefined ? `${config.title}.` : `${config.title}. ${config.body}`;
@@ -350,17 +350,11 @@ export const dismiss = (model: Model, entryId: string): StepReturn =>
350
350
 
351
351
  // VIEW
352
352
 
353
- const TONE_CLASS: Record<Tone, string> = {
354
- Ok: "notice toast ok",
355
- Error: "notice toast error",
356
- Quiet: "notice toast quiet",
357
- };
358
-
359
353
  const slipView = (entry: Stack.Entry, h: HtmlBuilder<Message>): Html => {
360
354
  const { tone, title, maybeBody, maybeAction } = entry.payload;
361
355
 
362
356
  return h.div(
363
- [h.Class(TONE_CLASS[tone])],
357
+ [h.Class(`notice toast${Notice.TONE_CLASS[tone]}`)],
364
358
  [
365
359
  Notice.toneGlyphView({ tone }, h),
366
360
  h.div([], [h.strong([], [title]), ...Option.toArray(maybeBody)]),
package/src/tokens.css CHANGED
@@ -81,6 +81,11 @@
81
81
  conic-gradient(at 1px 1px, #0000 75%, var(--dither-dot) 0) 0 0 / 4px 4px,
82
82
  conic-gradient(at 1px 1px, #0000 75%, var(--dither-dot) 0) 2px 2px / 4px 4px, var(--dither-base);
83
83
 
84
+ /* ---------- colour: fades ---------- */
85
+ --fade-on-ink: 0.75; /* what prints muted on paper, printed in paper on an inverted fill */
86
+ --fade-held: 0.6; /* a held control on an ink title bar */
87
+ --fade-dragged: 0.4; /* a held item, dimmed in place while its copy follows the pointer */
88
+
84
89
  /* ---------- type: three families ---------- */
85
90
  --font-px: "Departure Mono", ui-monospace, Menlo, Monaco, monospace;
86
91
  --font-body: system-ui, "Helvetica Neue", Helvetica, Arial, sans-serif;
@@ -122,6 +127,7 @@
122
127
  --deck: 1160px; /* the widest a deck of panels runs */
123
128
  --rail: 272px; /* the column of panels beside a deck, until the reader sizes it */
124
129
  --rail-drawer: 320px; /* the rail as a drawer, whatever width the reader gave it */
130
+ --rail-folded: 64px; /* the rail folded to its first panel's bar, stood on end */
125
131
 
126
132
  /* ---------- space: between components (Stack, Group, Wrap) ---------- */
127
133
  --space-s: 8px; /* things in one line: buttons, chips, a glyph and its word */
@@ -147,6 +153,7 @@
147
153
 
148
154
  /* ---------- the bench: a page whose work is the window ---------- */
149
155
  --bench-work-min: 152px; /* the least of its height the work keeps: a 120px pane, 16px gutters */
156
+ --bench-head-min: 64px; /* the least of its head a short window keeps: its padding and a small button */
150
157
 
151
158
  /* ---------- charts ---------- */
152
159
  --chart-plot: 160px; /* a line chart's plot, between its top line and its axis */
@@ -4,7 +4,6 @@ import type { Html, HtmlBuilder } from "foldkit/html";
4
4
  import { defineMessageUnion } from "foldkit/message";
5
5
  import * as Mount from "foldkit/mount";
6
6
 
7
- import type { Attributes } from "../attributes/attributes.ts";
8
7
  import { isPlain } from "../keys/keys.ts";
9
8
 
10
9
  // MODEL
@@ -172,8 +171,6 @@ export type ViewConfig<ParentMessage> = Readonly<{
172
171
  * Scene, resolve the Mount by name with the lifted Message:
173
172
  * `Scene.Mount.resolve({ name: Toolbar.BindToolKeys.name }, toParentMessage(Toolbar.Message.MovedToolFocus({ move })))`. */
174
173
  toParentMessage: (message: Message) => ParentMessage;
175
- /** Further attributes for the toolbar, never a class. */
176
- attributes?: Attributes<ParentMessage>;
177
174
  }>;
178
175
 
179
176
  /** A strip of tools that is one stop for Tab (`role="toolbar"`, named by its label, with its
@@ -189,12 +186,10 @@ export const view = <ParentMessage>(
189
186
  orientation = "Horizontal",
190
187
  isFloating = false,
191
188
  toParentMessage,
192
- attributes = [],
193
189
  } = config;
194
190
 
195
191
  return h.div(
196
192
  [
197
- ...attributes,
198
193
  h.Role("toolbar"),
199
194
  h.AriaLabel(label),
200
195
  h.AriaOrientation(orientation === "Vertical" ? "vertical" : "horizontal"),
@@ -1,6 +0,0 @@
1
- import { Option } from "effect";
2
- /** What Enter or leaving an inline field writes: the draft trimmed, or none (an untouched
3
- * draft, so an edit made elsewhere to `value` is never overwritten; an empty one; or the
4
- * text it replaces). */
5
- export declare const toWrite: (maybeDraft: Option.Option<string>, value: string) => Option.Option<string>;
6
- //# sourceMappingURL=inline.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"inline.d.ts","sourceRoot":"","sources":["../../../src/inlineInput/inline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAI9C;;yBAEyB;AACzB,eAAO,MAAM,OAAO,eAAgB,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAK5F,CAAC"}
@@ -1,7 +0,0 @@
1
- import { brandViewResult as __foldkitBrandViewResult } from 'foldkit/brand'
2
- import { Option, String, pipe } from "effect";
3
- // RULES
4
- /** What Enter or leaving an inline field writes: the draft trimmed, or none (an untouched
5
- * draft, so an edit made elsewhere to `value` is never overwritten; an empty one; or the
6
- * text it replaces). */
7
- export const toWrite = (maybeDraft, value) => __foldkitBrandViewResult((pipe(maybeDraft, Option.map(String.trim), Option.filter((text) => __foldkitBrandViewResult((String.isNonEmpty(text) && text !== value), "@gjermundgaraba-designs/pixel-paper/src/inlineInput/inline.js#anonymous")))), "@gjermundgaraba-designs/pixel-paper/src/inlineInput/inline.js#toWrite");
@@ -1,13 +0,0 @@
1
- import { Option, String, pipe } from "effect";
2
-
3
- // RULES
4
-
5
- /** What Enter or leaving an inline field writes: the draft trimmed, or none (an untouched
6
- * draft, so an edit made elsewhere to `value` is never overwritten; an empty one; or the
7
- * text it replaces). */
8
- export const toWrite = (maybeDraft: Option.Option<string>, value: string): Option.Option<string> =>
9
- pipe(
10
- maybeDraft,
11
- Option.map(String.trim),
12
- Option.filter((text) => String.isNonEmpty(text) && text !== value),
13
- );