@gjermundgaraba-designs/pixel-paper 0.18.0 → 0.20.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 (41) hide show
  1. package/DESIGN.md +26 -5
  2. package/dist/src/commandPalette/commandPalette.d.ts +2 -2
  3. package/dist/src/commandPalette/commandPalette.d.ts.map +1 -1
  4. package/dist/src/commandPalette/commandPalette.js +66 -42
  5. package/dist/src/commandPalette/match.d.ts +15 -5
  6. package/dist/src/commandPalette/match.d.ts.map +1 -1
  7. package/dist/src/commandPalette/match.js +17 -2
  8. package/dist/src/drag/drag.d.ts +3 -0
  9. package/dist/src/drag/drag.d.ts.map +1 -1
  10. package/dist/src/drag/drag.js +14 -10
  11. package/dist/src/index.d.ts +1 -0
  12. package/dist/src/index.d.ts.map +1 -1
  13. package/dist/src/index.js +1 -0
  14. package/dist/src/reel/index.d.ts +4 -0
  15. package/dist/src/reel/index.d.ts.map +1 -0
  16. package/dist/src/reel/index.js +2 -0
  17. package/dist/src/reel/layout.d.ts +180 -0
  18. package/dist/src/reel/layout.d.ts.map +1 -0
  19. package/dist/src/reel/layout.js +370 -0
  20. package/dist/src/reel/reel.d.ts +1158 -0
  21. package/dist/src/reel/reel.d.ts.map +1 -0
  22. package/dist/src/reel/reel.js +922 -0
  23. package/dist/src/sortable/sortable.d.ts +30 -30
  24. package/dist/src/sortable/sortable.d.ts.map +1 -1
  25. package/dist/src/sortable/sortable.js +2 -10
  26. package/dist/src/toast/toast.d.ts +12 -12
  27. package/dist/src/window/window.d.ts +2 -0
  28. package/dist/src/window/window.d.ts.map +1 -1
  29. package/dist/src/window/window.js +2 -1
  30. package/package.json +1 -1
  31. package/src/commandPalette/commandPalette.ts +71 -24
  32. package/src/commandPalette/match.ts +35 -6
  33. package/src/drag/drag.ts +19 -4
  34. package/src/index.ts +1 -0
  35. package/src/pixel-paper.css +236 -2
  36. package/src/reel/index.ts +20 -0
  37. package/src/reel/layout.ts +602 -0
  38. package/src/reel/reel.ts +1597 -0
  39. package/src/sortable/sortable.ts +8 -16
  40. package/src/tokens.css +5 -0
  41. package/src/window/window.ts +4 -0
@@ -245,6 +245,8 @@ export declare const update: (model: Model, message: Message) => Readonly<{
245
245
  }>;
246
246
  };
247
247
  commands?: Update.Commands<{
248
+ readonly _tag: "PressedEscape";
249
+ } | {
248
250
  readonly _tag: "CancelledPointer";
249
251
  } | {
250
252
  readonly _tag: "CompletedAnnounceReorder";
@@ -253,6 +255,9 @@ export declare const update: (model: Model, message: Message) => Readonly<{
253
255
  } | {
254
256
  readonly _tag: "GotDragAndDropMessage";
255
257
  readonly message: {
258
+ readonly _tag: "PressedArrowKey";
259
+ readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
260
+ } | {
256
261
  readonly _tag: "ActivatedKeyboardDrag";
257
262
  readonly itemId: string;
258
263
  readonly containerId: string;
@@ -279,9 +284,6 @@ export declare const update: (model: Model, message: Message) => Readonly<{
279
284
  readonly containerId: string;
280
285
  readonly index: number;
281
286
  }>;
282
- } | {
283
- readonly _tag: "PressedArrowKey";
284
- readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
285
287
  } | {
286
288
  readonly _tag: "PressedDraggable";
287
289
  readonly itemId: string;
@@ -299,8 +301,6 @@ export declare const update: (model: Model, message: Message) => Readonly<{
299
301
  readonly clientX: number;
300
302
  readonly clientY: number;
301
303
  readonly maybeIndex: Option.Option<number>;
302
- } | {
303
- readonly _tag: "PressedEscape";
304
304
  } | {
305
305
  readonly _tag: "PressedGripKey";
306
306
  readonly itemId: string;
@@ -413,6 +413,8 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
413
413
  }>;
414
414
  }>;
415
415
  }, {
416
+ readonly _tag: "PressedEscape";
417
+ } | {
416
418
  readonly _tag: "CancelledPointer";
417
419
  } | {
418
420
  readonly _tag: "CompletedAnnounceReorder";
@@ -421,6 +423,9 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
421
423
  } | {
422
424
  readonly _tag: "GotDragAndDropMessage";
423
425
  readonly message: {
426
+ readonly _tag: "PressedArrowKey";
427
+ readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
428
+ } | {
424
429
  readonly _tag: "ActivatedKeyboardDrag";
425
430
  readonly itemId: string;
426
431
  readonly containerId: string;
@@ -447,9 +452,6 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
447
452
  readonly containerId: string;
448
453
  readonly index: number;
449
454
  }>;
450
- } | {
451
- readonly _tag: "PressedArrowKey";
452
- readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
453
455
  } | {
454
456
  readonly _tag: "PressedDraggable";
455
457
  readonly itemId: string;
@@ -467,8 +469,6 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
467
469
  readonly clientX: number;
468
470
  readonly clientY: number;
469
471
  readonly maybeIndex: Option.Option<number>;
470
- } | {
471
- readonly _tag: "PressedEscape";
472
472
  } | {
473
473
  readonly _tag: "PressedGripKey";
474
474
  readonly itemId: string;
@@ -559,6 +559,8 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
559
559
  }>;
560
560
  }>;
561
561
  }, {
562
+ readonly _tag: "PressedEscape";
563
+ } | {
562
564
  readonly _tag: "CancelledPointer";
563
565
  } | {
564
566
  readonly _tag: "CompletedAnnounceReorder";
@@ -567,6 +569,9 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
567
569
  } | {
568
570
  readonly _tag: "GotDragAndDropMessage";
569
571
  readonly message: {
572
+ readonly _tag: "PressedArrowKey";
573
+ readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
574
+ } | {
570
575
  readonly _tag: "ActivatedKeyboardDrag";
571
576
  readonly itemId: string;
572
577
  readonly containerId: string;
@@ -593,9 +598,6 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
593
598
  readonly containerId: string;
594
599
  readonly index: number;
595
600
  }>;
596
- } | {
597
- readonly _tag: "PressedArrowKey";
598
- readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
599
601
  } | {
600
602
  readonly _tag: "PressedDraggable";
601
603
  readonly itemId: string;
@@ -613,8 +615,6 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
613
615
  readonly clientX: number;
614
616
  readonly clientY: number;
615
617
  readonly maybeIndex: Option.Option<number>;
616
- } | {
617
- readonly _tag: "PressedEscape";
618
618
  } | {
619
619
  readonly _tag: "PressedGripKey";
620
620
  readonly itemId: string;
@@ -710,6 +710,8 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
710
710
  }>;
711
711
  }>;
712
712
  }, {
713
+ readonly _tag: "PressedEscape";
714
+ } | {
713
715
  readonly _tag: "CancelledPointer";
714
716
  } | {
715
717
  readonly _tag: "CompletedAnnounceReorder";
@@ -718,6 +720,9 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
718
720
  } | {
719
721
  readonly _tag: "GotDragAndDropMessage";
720
722
  readonly message: {
723
+ readonly _tag: "PressedArrowKey";
724
+ readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
725
+ } | {
721
726
  readonly _tag: "ActivatedKeyboardDrag";
722
727
  readonly itemId: string;
723
728
  readonly containerId: string;
@@ -744,9 +749,6 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
744
749
  readonly containerId: string;
745
750
  readonly index: number;
746
751
  }>;
747
- } | {
748
- readonly _tag: "PressedArrowKey";
749
- readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
750
752
  } | {
751
753
  readonly _tag: "PressedDraggable";
752
754
  readonly itemId: string;
@@ -764,8 +766,6 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
764
766
  readonly clientX: number;
765
767
  readonly clientY: number;
766
768
  readonly maybeIndex: Option.Option<number>;
767
- } | {
768
- readonly _tag: "PressedEscape";
769
769
  } | {
770
770
  readonly _tag: "PressedGripKey";
771
771
  readonly itemId: string;
@@ -857,6 +857,8 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
857
857
  }>;
858
858
  }>;
859
859
  }, {
860
+ readonly _tag: "PressedEscape";
861
+ } | {
860
862
  readonly _tag: "CancelledPointer";
861
863
  } | {
862
864
  readonly _tag: "CompletedAnnounceReorder";
@@ -865,6 +867,9 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
865
867
  } | {
866
868
  readonly _tag: "GotDragAndDropMessage";
867
869
  readonly message: {
870
+ readonly _tag: "PressedArrowKey";
871
+ readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
872
+ } | {
868
873
  readonly _tag: "ActivatedKeyboardDrag";
869
874
  readonly itemId: string;
870
875
  readonly containerId: string;
@@ -891,9 +896,6 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
891
896
  readonly containerId: string;
892
897
  readonly index: number;
893
898
  }>;
894
- } | {
895
- readonly _tag: "PressedArrowKey";
896
- readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
897
899
  } | {
898
900
  readonly _tag: "PressedDraggable";
899
901
  readonly itemId: string;
@@ -911,8 +913,6 @@ export declare const subscriptions: <Prefix extends string>(keyPrefix: Prefix) =
911
913
  readonly clientX: number;
912
914
  readonly clientY: number;
913
915
  readonly maybeIndex: Option.Option<number>;
914
- } | {
915
- readonly _tag: "PressedEscape";
916
916
  } | {
917
917
  readonly _tag: "PressedGripKey";
918
918
  readonly itemId: string;
@@ -1055,6 +1055,8 @@ export declare const view: import("foldkit/submodel").View<{
1055
1055
  }>;
1056
1056
  }>;
1057
1057
  }, {
1058
+ readonly _tag: "PressedEscape";
1059
+ } | {
1058
1060
  readonly _tag: "CancelledPointer";
1059
1061
  } | {
1060
1062
  readonly _tag: "CompletedAnnounceReorder";
@@ -1063,6 +1065,9 @@ export declare const view: import("foldkit/submodel").View<{
1063
1065
  } | {
1064
1066
  readonly _tag: "GotDragAndDropMessage";
1065
1067
  readonly message: {
1068
+ readonly _tag: "PressedArrowKey";
1069
+ readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
1070
+ } | {
1066
1071
  readonly _tag: "ActivatedKeyboardDrag";
1067
1072
  readonly itemId: string;
1068
1073
  readonly containerId: string;
@@ -1089,9 +1094,6 @@ export declare const view: import("foldkit/submodel").View<{
1089
1094
  readonly containerId: string;
1090
1095
  readonly index: number;
1091
1096
  }>;
1092
- } | {
1093
- readonly _tag: "PressedArrowKey";
1094
- readonly direction: "Down" | "Left" | "NextContainer" | "PreviousContainer" | "Right" | "Up";
1095
1097
  } | {
1096
1098
  readonly _tag: "PressedDraggable";
1097
1099
  readonly itemId: string;
@@ -1109,8 +1111,6 @@ export declare const view: import("foldkit/submodel").View<{
1109
1111
  readonly clientX: number;
1110
1112
  readonly clientY: number;
1111
1113
  readonly maybeIndex: Option.Option<number>;
1112
- } | {
1113
- readonly _tag: "PressedEscape";
1114
1114
  } | {
1115
1115
  readonly _tag: "PressedGripKey";
1116
1116
  readonly itemId: string;
@@ -1 +1 @@
1
- {"version":3,"file":"sortable.d.ts","sourceRoot":"","sources":["../../../src/sortable/sortable.ts"],"names":[],"mappings":"AACA,OAAO,EAAS,MAAM,EAAS,MAAM,EAAE,MAAM,EAAE,MAAM,EAAQ,MAAM,QAAQ,CAAC;AAC5E,OAAO,KAAK,OAAO,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAAa,IAAI,EAAe,MAAM,cAAc,CAAC;AAEjE,OAAO,KAAK,KAAK,MAAM,eAAe,CAAC;AAGvC,OAAO,KAAK,YAAY,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,MAAM,MAAM,gBAAgB,CAAC;AAyBzC;oDACoD;AACpD,eAAO,MAAM,IAAI;;;EAA6D,CAAC;AAC/E,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAC;AAiCpC;iFACiF;AACjF,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAIhB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAC;AAItC,oDAAoD;AACpD,eAAO,MAAM,OAAO;;QACO,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAE9B,MAAM;QACN,SAAS;QACT,OAAO;QACP,OAAO;QACP,OAAO;QACP,OAAO;QACP,KAAK;;;;;;QAEW,MAAM;QAAiB,KAAK;;;;;;QAE5C,OAAO;QACP,OAAO;QACP,OAAO;QACP,OAAO;QACP,UAAU;;;QAGV,OAAO;QACP,OAAO;QACP,OAAO;QACP,OAAO;QACP,UAAU;;;;;;EAMZ,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAC;AAI1C;;;;0EAI0E;AAC1E,eAAO,MAAM,UAAU;;QAEnB,MAAM;QACN,SAAS;QACT,OAAO;QACP,gBAAgB;QAChB,iBAAiB;;EAEnB,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;AAIhD;6CAC6C;AAC7C,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AAElD,kEAAkE;AAClE,eAAO,MAAM,IAAI,WAAY,UAAU,KAAG,KAIxC,CAAC;AAOH,0CAA0C;AAC1C,eAAO,MAAM,SAAS,UAAW,KAAK,KAAG,OAC2B,CAAC;AAUrE,2FAA2F;AAC3F,eAAO,MAAM,eAAe;;;;iBAK1B,CAAC;AAIH,KAAK,YAAY,GAAG,MAAM,CAAC,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;AAuN5E,wFAAwF;AACxF,eAAO,MAAM,MAAM,UAAW,KAAK,WAAW,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAwCjD,CAAC;AAIL;6DAC6D;AAC7D,eAAO,MAAM,UAAU,UAAW,KAAK,aAAa,OAAO,KAAG,YAG7D,CAAC;AAoBF,QAAA,MAAM,WAAW;;;;EAIf,CAAC;AACH,KAAK,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAC;AAU3C,8FAA8F;AAC9F,eAAO,MAAM,eAAe,SAAU,WAAW,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAgBxE,CAAC;AAmDF;;;;6CAI6C;AAC7C,eAAO,MAAM,aAAa,GAAI,MAAM,SAAS,MAAM,aAAa,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mGACxB,CAAC;AAI/C;0EAC0E;AAC1E,eAAO,MAAM,cAAc;;;;;;EAQzB,CAAC;AAIH,4FAA4F;AAC5F,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEhD,sDAAsD;AACtD,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC;4CACwC;IACxC,QAAQ,EAAE,OAAO,CAAC;IAClB;yEACqE;IACrE,IAAI,EAAE,IAAI,CAAC;CACZ,CAAC,CAAC;AAEH;kEACkE;AAClE,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAChC,KAAK,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAC3B,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd;;gFAE4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;mDAC+C;IAC/C,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;6BACyB;IACzB,UAAU,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,KAAK,aAAa,CAAC,IAAI,CAAC,CAAC;CACrE,CAAC,CAAC;AAsNH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAjPR,aAAa,CAAC,IAAI,CAAC;IAC1B,yCAAyC;WAClC,MAAM;IACb;;gFAE4E;aACnE,MAAM;IACf;mDAC+C;iBAClC,OAAO;IACpB;6BACyB;gBACb,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,KAAK,aAAa,CAAC,IAAI,CAAC;GAkQnE,CAAC"}
1
+ {"version":3,"file":"sortable.d.ts","sourceRoot":"","sources":["../../../src/sortable/sortable.ts"],"names":[],"mappings":"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"}
@@ -10,7 +10,7 @@ import * as Subscription from "foldkit/subscription";
10
10
  import * as Update from "foldkit/update";
11
11
  import * as Announcer from "../announcer/announcer.js";
12
12
  import * as Button from "../button/button.js";
13
- import { LEFT_BUTTON, activatedKeyboardDrag, cancelledDrag, escapeKeys, insertionIndex, isCarrying, markDragging, measureLift, movedPointer, phaseOf, pressedDraggable, releasedPointer, } from "../drag/drag.js";
13
+ import { LEFT_BUTTON, activatedKeyboardDrag, cancelledDrag, escapeKeys, insertionIndex, isCarrying, markDragging, measureLift, movedPointer, phaseOf, pressedDraggable, pressingPointer, releasedPointer, } from "../drag/drag.js";
14
14
  import * as Glyph from "../glyph/glyph.js";
15
15
  import * as IconButton from "../iconButton/iconButton.js";
16
16
  import { LOCALE } from "../locale/locale.js";
@@ -289,15 +289,7 @@ const pointerAt = (hold, event) => (__foldkitBrandViewResult(({
289
289
  maybeIndex: pointerIndex(hold.listId, hold.heldHook, event.clientX, event.clientY),
290
290
  }), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#pointerAt"));
291
291
  /** The pressing pointer's moves, release and cancel; another pointer's are not the list's. */
292
- export const pointerMessages = (hold) => {
293
- const fromPressing = (type) => __foldkitBrandViewResult((Stream.fromEventListener(document, type).pipe(Stream.filter((event) => __foldkitBrandViewResult((event.pointerId === hold.pointerId), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#anonymous~25")))), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#fromPressing");
294
- const streams = [
295
- Stream.map(fromPressing("pointermove"), (event) => __foldkitBrandViewResult((Message.MovedPointer(pointerAt(hold, event))), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#anonymous~26")),
296
- Stream.map(fromPressing("pointerup"), (event) => __foldkitBrandViewResult((Message.ReleasedPointer(pointerAt(hold, event))), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#anonymous~27")),
297
- Stream.map(fromPressing("pointercancel"), () => __foldkitBrandViewResult((Message.CancelledPointer()), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#anonymous~28")),
298
- ];
299
- return __foldkitBrandViewResult((Stream.mergeAll(streams, { concurrency: "unbounded" })), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#pointerMessages");
300
- };
292
+ export const pointerMessages = (hold) => __foldkitBrandViewResult((Stream.map(pressingPointer(hold.pointerId), (event) => __foldkitBrandViewResult((Match.value(event.type).pipe(Match.when("pointermove", () => __foldkitBrandViewResult((Message.MovedPointer(pointerAt(hold, event))), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#anonymous~26")), Match.when("pointerup", () => __foldkitBrandViewResult((Message.ReleasedPointer(pointerAt(hold, event))), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#anonymous~27")), Match.orElse(() => __foldkitBrandViewResult((Message.CancelledPointer()), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#anonymous~28")))), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#anonymous~25"))), "@gjermundgaraba-designs/pixel-paper/src/sortable/sortable.js#pointerMessages");
301
293
  const ownSubscriptions = Subscription.make()((entry) => (__foldkitBrandViewResult(({
302
294
  pointer: entry({ maybeHold: Schema.Option(PointerHold) }, {
303
295
  modelToDependencies: (model) => (__foldkitBrandViewResult(({
@@ -326,8 +326,6 @@ export declare const update: (model: Model, message: Message) => Readonly<{
326
326
  readonly pendingDismissVersion: number;
327
327
  readonly isHovered: boolean;
328
328
  readonly swipeState: {
329
- readonly _tag: "Idle";
330
- } | {
331
329
  readonly _tag: "Dismissing";
332
330
  readonly offsetX: number;
333
331
  readonly direction: "Left" | "Right";
@@ -336,6 +334,8 @@ export declare const update: (model: Model, message: Message) => Readonly<{
336
334
  readonly pointerId: number;
337
335
  readonly startX: number;
338
336
  readonly currentX: number;
337
+ } | {
338
+ readonly _tag: "Idle";
339
339
  } | {
340
340
  readonly _tag: "Settling";
341
341
  readonly offsetX: number;
@@ -371,15 +371,15 @@ export declare const update: (model: Model, message: Message) => Readonly<{
371
371
  readonly _tag: "GotAnimationMessage";
372
372
  readonly entryId: string;
373
373
  readonly message: {
374
- readonly _tag: "Hid";
375
- } | {
376
- readonly _tag: "Showed";
377
- } | {
378
374
  readonly _tag: "CompletedWaitForPaint";
379
375
  readonly generation: number;
380
376
  } | {
381
377
  readonly _tag: "EndedAnimation";
382
378
  readonly generation: number;
379
+ } | {
380
+ readonly _tag: "Hid";
381
+ } | {
382
+ readonly _tag: "Showed";
383
383
  };
384
384
  } | {
385
385
  readonly _tag: "HoveredEntry";
@@ -557,8 +557,6 @@ export declare const view: import("foldkit/submodel").View<{
557
557
  readonly pendingDismissVersion: number;
558
558
  readonly isHovered: boolean;
559
559
  readonly swipeState: {
560
- readonly _tag: "Idle";
561
- } | {
562
560
  readonly _tag: "Dismissing";
563
561
  readonly offsetX: number;
564
562
  readonly direction: "Left" | "Right";
@@ -567,6 +565,8 @@ export declare const view: import("foldkit/submodel").View<{
567
565
  readonly pointerId: number;
568
566
  readonly startX: number;
569
567
  readonly currentX: number;
568
+ } | {
569
+ readonly _tag: "Idle";
570
570
  } | {
571
571
  readonly _tag: "Settling";
572
572
  readonly offsetX: number;
@@ -602,15 +602,15 @@ export declare const view: import("foldkit/submodel").View<{
602
602
  readonly _tag: "GotAnimationMessage";
603
603
  readonly entryId: string;
604
604
  readonly message: {
605
- readonly _tag: "Hid";
606
- } | {
607
- readonly _tag: "Showed";
608
- } | {
609
605
  readonly _tag: "CompletedWaitForPaint";
610
606
  readonly generation: number;
611
607
  } | {
612
608
  readonly _tag: "EndedAnimation";
613
609
  readonly generation: number;
610
+ } | {
611
+ readonly _tag: "Hid";
612
+ } | {
613
+ readonly _tag: "Showed";
614
614
  };
615
615
  } | {
616
616
  readonly _tag: "HoveredEntry";
@@ -6,6 +6,8 @@ export type ViewConfig<Message> = Readonly<{
6
6
  id: string;
7
7
  /** Pixel caps in the inverted title bar. */
8
8
  title: string;
9
+ /** A quiet IconButton before the kind glyph: a Reel's grip. */
10
+ lead?: ReadonlyArray<Html>;
9
11
  /** What kind of thing it is: a terminal, a note, a machine. */
10
12
  glyph?: GlyphName;
11
13
  /** A chip in the title bar: its state. */
@@ -1 +1 @@
1
- {"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../../src/window/window.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAOpD,wDAAwD;AACxD,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACtC,sDAAsD;IACtD,KAAK,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAC5B,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACnC,iEAAiE;IACjE,IAAI,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACpC,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC,CAAC;AAEH;;6DAE6D;AAC7D,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IAoCpF,CAAC"}
1
+ {"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../../src/window/window.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAOpD,wDAAwD;AACxD,MAAM,MAAM,UAAU,CAAC,OAAO,IAAI,QAAQ,CAAC;IACzC,oFAAoF;IACpF,EAAE,EAAE,MAAM,CAAC;IACX,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,IAAI,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAC3B,+DAA+D;IAC/D,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACtC,sDAAsD;IACtD,KAAK,CAAC,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;IAC5B,oEAAoE;IACpE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACnC,iEAAiE;IACjE,IAAI,CAAC,EAAE,aAAa,CAAC,IAAI,GAAG,MAAM,CAAC,CAAC;IACpC,8DAA8D;IAC9D,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC,CAAC;AAEH;;6DAE6D;AAC7D,eAAO,MAAM,IAAI,GAAI,OAAO,UAAU,UAAU,CAAC,OAAO,CAAC,KAAK,WAAW,CAAC,OAAO,CAAC,KAAG,IAsCpF,CAAC"}
@@ -7,13 +7,14 @@ import { slotView } from "../slot/slot.js";
7
7
  * its body scrolls inside, and its title bar carries its kind, its state and its tools. A
8
8
  * region landmark (`<section>`) named by its `h2` title. */
9
9
  export const view = (config, h) => {
10
- const { id, title, glyph, status = [], tools = [], onClose, body, foot = [], isFlush = false, } = config;
10
+ const { id, title, lead = [], glyph, status = [], tools = [], onClose, body, foot = [], isFlush = false, } = config;
11
11
  return __foldkitBrandViewResult((h.section([
12
12
  h.Id(id),
13
13
  h.AriaLabelledBy(titleId(id)),
14
14
  h.Class(isFlush ? "panel window flush" : "panel window"),
15
15
  ], [
16
16
  h.header([h.Class("panel-tb")], [
17
+ ...slotView("span", "panel-lead", lead, h),
17
18
  ...(glyph === undefined ? [] : [Glyph.view({ name: glyph }, h)]),
18
19
  h.h2([h.Id(titleId(id))], [title]),
19
20
  ...status,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjermundgaraba-designs/pixel-paper",
3
- "version": "0.18.0",
3
+ "version": "0.20.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,5 +1,5 @@
1
1
  import { Input as UiInput } from "@foldkit/ui";
2
- import { Array, Effect, Match, Option, Schema, Stream, String, pipe } from "effect";
2
+ import { Array, Effect, HashSet, Match, Option, Schema, Stream, String, pipe } from "effect";
3
3
  import * as FoldkitCommand from "foldkit/command";
4
4
  import * as Dom from "foldkit/dom";
5
5
  import type { Attribute, Html, HtmlBuilder, KeyboardModifiers } from "foldkit/html";
@@ -27,7 +27,7 @@ import {
27
27
  earlyKeys,
28
28
  shownPageOf,
29
29
  } from "./keys.ts";
30
- import { findChoices, findCommands } from "./match.ts";
30
+ import { findCommands, pageChoices } from "./match.ts";
31
31
  import type { Choice, Command, Page } from "./match.ts";
32
32
  import { Message } from "./message.ts";
33
33
  import type { ArrowKey, ShownRow } from "./message.ts";
@@ -571,6 +571,8 @@ type ShownPageState = Readonly<{ commandId: string; page: Page; state: OpenPage
571
571
  type Shown = Readonly<{
572
572
  maybePage: Option.Option<ShownPageState>;
573
573
  rows: ReadonlyArray<Choice | Command>;
574
+ /** The rows a page's query keeps only to show where a row it finds sits in the tree. */
575
+ contextIds: HashSet.HashSet<string>;
574
576
  maybeCursorId: Option.Option<string>;
575
577
  }>;
576
578
 
@@ -581,16 +583,17 @@ const pageOf = (commands: ReadonlyArray<Command>, state: OpenPage): Option.Optio
581
583
  Option.map((page) => ({ commandId: state.commandId, page, state })),
582
584
  );
583
585
 
584
- const cursorOn = (
586
+ const isListed = (rows: ReadonlyArray<Choice | Command>, rowId: string): boolean =>
587
+ Array.some(rows, (row) => row.id === rowId);
588
+
589
+ const firstListed = (
585
590
  rows: ReadonlyArray<Choice>,
586
591
  candidates: ReadonlyArray<Option.Option<string>>,
587
592
  ): Option.Option<string> =>
588
- Option.orElse(
589
- Array.findFirst(Array.getSomes(candidates), (cursorId) =>
590
- Array.some(rows, (row) => row.id === cursorId),
591
- ),
592
- () => Option.map(Array.head(rows), (row) => row.id),
593
- );
593
+ Array.findFirst(Array.getSomes(candidates), (cursorId) => isListed(rows, cursorId));
594
+
595
+ const firstId = (rows: ReadonlyArray<Choice | Command>): Option.Option<string> =>
596
+ Option.map(Array.head(rows), (row) => row.id);
594
597
 
595
598
  const shifted = (shown: Shown, shift: number): Shown => ({
596
599
  ...shown,
@@ -610,34 +613,51 @@ const shownOf = (model: Model, commands: ReadonlyArray<Command>): Shown => {
610
613
  };
611
614
 
612
615
  // What the view shows of the Model with `commands`: the open page while its command is
613
- // listed, the rows its query finds, and the cursor's row: the row the reader moved it to
614
- // while that row is listed, else the page's `start`, else its `current`, else the first row;
615
- // none when nothing is listed.
616
+ // listed, the rows its query finds (on a tree page, with the rows they sit under), and the
617
+ // cursor's row: the row the reader moved it to while that row is listed, else the page's
618
+ // `start`, else its `current`, else the first row, these three only among the rows the query
619
+ // finds; none when nothing is listed.
616
620
  const unshiftedOf = (model: Model, commands: ReadonlyArray<Command>): Shown => {
617
621
  const maybePage = Option.flatMap(model.maybePage, (state) => pageOf(commands, state));
618
622
  return Option.match(maybePage, {
619
623
  onNone: () => {
620
624
  const rows = findCommands(commands, model.query);
621
- return { maybePage, rows, maybeCursorId: cursorOn(rows, [model.maybeCursor]) };
625
+ return {
626
+ maybePage,
627
+ rows,
628
+ contextIds: HashSet.empty(),
629
+ maybeCursorId: pipe(
630
+ Option.filter(model.maybeCursor, (rowId) => isListed(rows, rowId)),
631
+ Option.orElse(() => firstId(rows)),
632
+ ),
633
+ };
622
634
  },
623
635
  onSome: ({ page, state }) => {
624
- const rows = findChoices(page, state.query);
636
+ const { rows, found } = pageChoices(page, state.query);
637
+ const idsOf = (choices: ReadonlyArray<Choice>) =>
638
+ HashSet.fromIterable(Array.map(choices, ({ id }) => id));
625
639
  return {
626
640
  maybePage,
627
641
  rows,
628
- maybeCursorId: cursorOn(rows, [
629
- state.maybeCursor,
630
- Option.fromNullishOr(page.start),
631
- Option.fromNullishOr(page.current),
632
- ]),
642
+ contextIds: HashSet.difference(idsOf(rows), idsOf(found)),
643
+ maybeCursorId: pipe(
644
+ Option.filter(state.maybeCursor, (rowId) => isListed(rows, rowId)),
645
+ Option.orElse(() =>
646
+ firstListed(found, [
647
+ Option.fromNullishOr(page.start),
648
+ Option.fromNullishOr(page.current),
649
+ ]),
650
+ ),
651
+ Option.orElse(() => firstId(found)),
652
+ ),
633
653
  };
634
654
  },
635
655
  });
636
656
  };
637
657
 
638
658
  /** The row the cursor is on as the view shows it with `commands`: the row the reader moved it
639
- * to while that row is listed, else the page's `start` or `current`, else the first row; none
640
- * when nothing is listed. Compare it before and after the caller's commands change to know
659
+ * to while that row is listed, else the page's `start` or `current` while the query finds it,
660
+ * else the first row the query finds; none when nothing is listed. Compare it before and after the caller's commands change to know
641
661
  * when the cursor fell to another row (its row left) and should be scrolled into view with
642
662
  * `ScrollToCursor`; a cursor whose row is still listed keeps it, and the list its scroll. */
643
663
  export const maybeCursorId = (
@@ -650,6 +670,12 @@ const isCommand = (row: Choice | Command): row is Command => "group" in row;
650
670
  const isCurrentRow = (shown: Shown, row: Choice | Command): boolean =>
651
671
  Option.exists(shown.maybePage, ({ page }) => page.current === row.id);
652
672
 
673
+ const isContextRow = (shown: Shown, row: Choice | Command): boolean =>
674
+ HashSet.has(shown.contextIds, row.id);
675
+
676
+ const levelOf = (row: Choice | Command): Option.Option<number> =>
677
+ "level" in row ? Option.fromNullishOr(row.level) : Option.none();
678
+
653
679
  // What a row is as this render shows it, which Enter acts on as a click would.
654
680
  const shownRowFor = (shown: Shown, row: Choice | Command): ShownRow => ({
655
681
  id: row.id,
@@ -718,7 +744,13 @@ const rowPartsView = (
718
744
  ...(row.glyph === undefined
719
745
  ? []
720
746
  : [h.span([h.Class("glyph")], [Glyph.view({ name: row.glyph }, h)])]),
721
- h.span([h.Class("palette-label")], [row.label]),
747
+ h.span(
748
+ [h.Class("palette-label")],
749
+ [
750
+ row.label,
751
+ ...(isContextRow(shown, row) ? [h.span([h.Class("sr-only")], [" (context)"])] : []),
752
+ ],
753
+ ),
722
754
  ...(Option.contains(model.maybePicking, row.id)
723
755
  ? [h.span([h.Class("cursor"), h.AriaHidden(true)])]
724
756
  : []),
@@ -745,7 +777,20 @@ const rowView = (
745
777
  ...(shownRow.isHeld ? [h.AriaDisabled(true)] : []),
746
778
  h.DataAttribute(ROW_ATTRIBUTE, row.id),
747
779
  ...(shownRow.hasPage ? [h.DataAttribute(OPENS_PAGE_ATTRIBUTE, "")] : []),
748
- h.Class(row.isAuthored === true ? "palette-row authored" : "palette-row"),
780
+ h.Class(
781
+ Array.join(
782
+ [
783
+ "palette-row",
784
+ ...(row.isAuthored === true ? ["authored"] : []),
785
+ ...(isContextRow(shown, row) ? ["context"] : []),
786
+ ],
787
+ " ",
788
+ ),
789
+ ),
790
+ ...Option.match(levelOf(row), {
791
+ onNone: () => [],
792
+ onSome: (level) => [h.Style({ "--palette-level": `${level}` })],
793
+ }),
749
794
  ...(shownRow.isHeld || isPicking(model)
750
795
  ? []
751
796
  : [h.OnClick(Message.ClickedRow({ row: shownRow, maybeShownPage: shownPageIdOf(shown) }))]),
@@ -898,7 +943,9 @@ const countView = (model: Model, shown: Shown, h: HtmlBuilder<Message>): Html =>
898
943
  const shownQuery = shownQueryOf(model, shown);
899
944
  return h.div(
900
945
  [h.Role("status"), h.Class("sr-only")],
901
- String.isEmpty(shownQuery) ? [] : [countWords(shown.rows.length)],
946
+ String.isEmpty(shownQuery)
947
+ ? []
948
+ : [countWords(shown.rows.length - HashSet.size(shown.contextIds))],
902
949
  );
903
950
  };
904
951
 
@@ -1,4 +1,4 @@
1
- import { Array, Option, Order, String, pipe } from "effect";
1
+ import { Array, HashSet, Option, Order, String, pipe } from "effect";
2
2
 
3
3
  import type { GlyphName } from "../glyph/glyphs.ts";
4
4
 
@@ -19,6 +19,9 @@ export type Choice = Readonly<{
19
19
  isDisabled?: boolean;
20
20
  /** Why it cannot run now (`"No item open"`), printed in place of the hint while held. */
21
21
  reason?: string;
22
+ /** On a page, its depth in the page's tree, from 1 at the roots: it sits under the nearest
23
+ * choice before it one level up, and indents by its depth. A page without levels is flat. */
24
+ level?: number;
22
25
  }>;
23
26
 
24
27
  /** A page of choices a command opens in place (a status, a date, a destination). */
@@ -30,15 +33,15 @@ export type Page = Readonly<{
30
33
  find?: string;
31
34
  /** The choice in force now: tagged current, and not picked again. */
32
35
  current?: string;
33
- /** Where the cursor starts while the reader has not moved it: this choice while it is
34
- * listed, else `current`, else the first choice (the next status after the current one; the
35
- * first a typed query finds). */
36
+ /** Where the cursor starts while the reader has not moved it: this choice while the query
37
+ * finds it, else `current` while the query finds it, else the first choice the query finds
38
+ * (the next status after the current one; the first a typed query finds). */
36
39
  start?: string;
37
40
  }>;
38
41
 
39
42
  /** One command, under its group's heading. With a `page` it opens that page in place instead
40
43
  * of being selected. */
41
- export type Command = Choice & Readonly<{ group: string; page?: Page }>;
44
+ export type Command = Omit<Choice, "level"> & Readonly<{ group: string; page?: Page }>;
42
45
 
43
46
  // MATCHING
44
47
 
@@ -75,8 +78,34 @@ export const findCommands = (
75
78
  return Array.sort(ordered, Order.mapInput(Order.Number, groupIndex));
76
79
  };
77
80
 
78
- /** The choices a page shows for its query. */
81
+ /** The choices a page's query finds. */
79
82
  export const findChoices = (page: Page, query: string): ReadonlyArray<Choice> =>
80
83
  String.isEmpty(String.trim(query))
81
84
  ? page.choices
82
85
  : Array.filter(page.choices, (choice) => matches(query, choice.label));
86
+
87
+ type Walk = Readonly<{ chain: ReadonlyArray<Choice>; keptIds: HashSet.HashSet<string> }>;
88
+
89
+ const WALK_START: Walk = { chain: [], keptIds: HashSet.empty() };
90
+
91
+ /** What a page shows for its query: the choices it finds, and the rows, which are those
92
+ * choices each with the choices it sits under in the page's tree, in the page's order. */
93
+ export type PageChoices = Readonly<{
94
+ rows: ReadonlyArray<Choice>;
95
+ found: ReadonlyArray<Choice>;
96
+ }>;
97
+
98
+ export const pageChoices = (page: Page, query: string): PageChoices => {
99
+ const found = findChoices(page, query);
100
+ const foundIds = HashSet.fromIterable(Array.map(found, ({ id }) => id));
101
+ const { keptIds } = Array.reduce(page.choices, WALK_START, ({ chain, keptIds }, choice): Walk => {
102
+ const nextChain = Array.append(Array.take(chain, (choice.level ?? 1) - 1), choice);
103
+ return {
104
+ chain: nextChain,
105
+ keptIds: HashSet.has(foundIds, choice.id)
106
+ ? HashSet.union(keptIds, HashSet.fromIterable(Array.map(nextChain, ({ id }) => id)))
107
+ : keptIds,
108
+ };
109
+ });
110
+ return { rows: Array.filter(page.choices, ({ id }) => HashSet.has(keptIds, id)), found };
111
+ };