efficient-shell 4.131.0 → 4.134.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 (111) hide show
  1. package/README.md +74 -12
  2. package/dist/{Browser-PSEMJFZE.js → Browser-O3QZGUF2.js} +11 -11
  3. package/dist/{Browser-PSEMJFZE.js.map → Browser-O3QZGUF2.js.map} +1 -1
  4. package/dist/{Calculator-ZXJPXG7F.js → Calculator-L6N5X3KU.js} +9 -9
  5. package/dist/{Calculator-ZXJPXG7F.js.map → Calculator-L6N5X3KU.js.map} +1 -1
  6. package/dist/ConfirmDialog-LYSSZSUD.js +4 -0
  7. package/dist/{ConfirmDialog-MTC7QMUJ.js.map → ConfirmDialog-LYSSZSUD.js.map} +1 -1
  8. package/dist/{CurrencyConverter-WSJ2BBSX.js → CurrencyConverter-64MKXB3D.js} +9 -9
  9. package/dist/{CurrencyConverter-WSJ2BBSX.js.map → CurrencyConverter-64MKXB3D.js.map} +1 -1
  10. package/dist/{Documents-PGMZR2MA.js → Documents-5WXXYCLI.js} +11 -11
  11. package/dist/{Documents-PGMZR2MA.js.map → Documents-5WXXYCLI.js.map} +1 -1
  12. package/dist/{Files-2LKCJGYW.js → Files-AUURUUDN.js} +17 -17
  13. package/dist/{Files-2LKCJGYW.js.map → Files-AUURUUDN.js.map} +1 -1
  14. package/dist/{Notepad-KW6WVABH.js → Notepad-GPXJVPB7.js} +12 -12
  15. package/dist/{Notepad-KW6WVABH.js.map → Notepad-GPXJVPB7.js.map} +1 -1
  16. package/dist/PdfViewer-HKO6P4SR.js +5 -0
  17. package/dist/{PdfViewer-M2YGZIYX.js.map → PdfViewer-HKO6P4SR.js.map} +1 -1
  18. package/dist/{PomodoroTimer-DG3LI5CX.js → PomodoroTimer-4TQBWLP2.js} +11 -11
  19. package/dist/{PomodoroTimer-DG3LI5CX.js.map → PomodoroTimer-4TQBWLP2.js.map} +1 -1
  20. package/dist/{Preview-E7EJJNXN.js → Preview-FOZYPKM2.js} +14 -14
  21. package/dist/{Preview-E7EJJNXN.js.map → Preview-FOZYPKM2.js.map} +1 -1
  22. package/dist/{Spreadsheet-IDUODRZU.js → Spreadsheet-3C66BBBF.js} +12 -12
  23. package/dist/{Spreadsheet-IDUODRZU.js.map → Spreadsheet-3C66BBBF.js.map} +1 -1
  24. package/dist/{Stock-YMPOMUAW.js → Stock-IF4IHYLU.js} +9 -9
  25. package/dist/{Stock-YMPOMUAW.js.map → Stock-IF4IHYLU.js.map} +1 -1
  26. package/dist/{Weather-NZO3CWLH.js → Weather-725PFR7T.js} +9 -9
  27. package/dist/{Weather-NZO3CWLH.js.map → Weather-725PFR7T.js.map} +1 -1
  28. package/dist/{WorldClock-HUC6D3AD.js → WorldClock-UU4NZIQ7.js} +9 -9
  29. package/dist/{WorldClock-HUC6D3AD.js.map → WorldClock-UU4NZIQ7.js.map} +1 -1
  30. package/dist/apps/index.d.ts +2 -2
  31. package/dist/apps/index.js +28 -28
  32. package/dist/apps/index.js.map +1 -1
  33. package/dist/brand.css +1 -1
  34. package/dist/{chunk-TQURTLK2.js → chunk-2TPTHAKI.js} +5 -5
  35. package/dist/{chunk-TQURTLK2.js.map → chunk-2TPTHAKI.js.map} +1 -1
  36. package/dist/{chunk-EENVXIVU.js → chunk-4P44JXDS.js} +3 -3
  37. package/dist/{chunk-EENVXIVU.js.map → chunk-4P44JXDS.js.map} +1 -1
  38. package/dist/{chunk-CT3HVZRZ.js → chunk-5O42JXQE.js} +2 -2
  39. package/dist/chunk-5O42JXQE.js.map +1 -0
  40. package/dist/{chunk-7IN7YZAL.js → chunk-72CQWN7O.js} +3 -3
  41. package/dist/chunk-72CQWN7O.js.map +1 -0
  42. package/dist/{chunk-I464GYJJ.js → chunk-BE7FZOFU.js} +2 -2
  43. package/dist/chunk-BE7FZOFU.js.map +1 -0
  44. package/dist/{chunk-3L463T5H.js → chunk-DVZWYWSD.js} +2 -2
  45. package/dist/chunk-DVZWYWSD.js.map +1 -0
  46. package/dist/{chunk-FONEQRBH.js → chunk-ET6ID4LP.js} +3 -3
  47. package/dist/{chunk-FONEQRBH.js.map → chunk-ET6ID4LP.js.map} +1 -1
  48. package/dist/{chunk-CGXMWHQW.js → chunk-GIGV2ZRW.js} +3 -3
  49. package/dist/{chunk-CGXMWHQW.js.map → chunk-GIGV2ZRW.js.map} +1 -1
  50. package/dist/{chunk-2GZQ352J.js → chunk-IULP5BCD.js} +5 -5
  51. package/dist/chunk-IULP5BCD.js.map +1 -0
  52. package/dist/{chunk-BJ34QOR3.js → chunk-OLJDJYSU.js} +287 -146
  53. package/dist/chunk-OLJDJYSU.js.map +1 -0
  54. package/dist/{chunk-7MA5XBWD.js → chunk-PIGDNN5F.js} +5 -5
  55. package/dist/{chunk-7MA5XBWD.js.map → chunk-PIGDNN5F.js.map} +1 -1
  56. package/dist/chunk-Q2MJVSJT.js +7 -0
  57. package/dist/{chunk-UHPWZ2F4.js.map → chunk-Q2MJVSJT.js.map} +1 -1
  58. package/dist/{chunk-ZKHD4J73.js → chunk-Q3LT745N.js} +10 -10
  59. package/dist/chunk-Q3LT745N.js.map +1 -0
  60. package/dist/{chunk-CLGNTGC5.js → chunk-QAGQRNKI.js} +3 -3
  61. package/dist/{chunk-CLGNTGC5.js.map → chunk-QAGQRNKI.js.map} +1 -1
  62. package/dist/{chunk-E3TWIN7Z.js → chunk-QRL64J5F.js} +3 -3
  63. package/dist/{chunk-E3TWIN7Z.js.map → chunk-QRL64J5F.js.map} +1 -1
  64. package/dist/{chunk-TFXJRHJV.js → chunk-QSKCQUZE.js} +3 -3
  65. package/dist/{chunk-TFXJRHJV.js.map → chunk-QSKCQUZE.js.map} +1 -1
  66. package/dist/{chunk-MQPX6ERJ.js → chunk-R4OQIQYX.js} +4 -4
  67. package/dist/{chunk-MQPX6ERJ.js.map → chunk-R4OQIQYX.js.map} +1 -1
  68. package/dist/{chunk-LQTVTOSY.js → chunk-RT6DSB5Y.js} +45 -19
  69. package/dist/chunk-RT6DSB5Y.js.map +1 -0
  70. package/dist/{chunk-VWZIGZFF.js → chunk-SHQM5CK3.js} +4 -4
  71. package/dist/{chunk-VWZIGZFF.js.map → chunk-SHQM5CK3.js.map} +1 -1
  72. package/dist/{chunk-KWEX4ZAF.js → chunk-T4VQHMG4.js} +3 -3
  73. package/dist/{chunk-KWEX4ZAF.js.map → chunk-T4VQHMG4.js.map} +1 -1
  74. package/dist/{chunk-BFBWLCO6.js → chunk-TW7LPMVR.js} +2 -2
  75. package/dist/chunk-TW7LPMVR.js.map +1 -0
  76. package/dist/{chunk-XH3ELPQZ.js → chunk-W622KQTD.js} +3 -3
  77. package/dist/chunk-W622KQTD.js.map +1 -0
  78. package/dist/{chunk-SEWF4O3H.js → chunk-X4Q6G253.js} +3 -3
  79. package/dist/chunk-X4Q6G253.js.map +1 -0
  80. package/dist/file-intake/index.js +2 -2
  81. package/dist/{index-COwksV6G.d.ts → index-DZ7E7bCl.d.ts} +196 -4
  82. package/dist/index.d.ts +3 -3
  83. package/dist/index.js +35 -35
  84. package/dist/index.js.map +1 -1
  85. package/dist/map/index.js +1 -1
  86. package/dist/markdown/index.d.ts +4 -4
  87. package/dist/markdown/index.js.map +1 -1
  88. package/dist/markup/index.d.ts +2 -2
  89. package/dist/markup/index.js.map +1 -1
  90. package/dist/page-tabs/index.js +3 -3
  91. package/dist/shell.css +1 -1
  92. package/dist/styles.css +4 -4
  93. package/dist/themes.css +3 -3
  94. package/dist/ui/index.d.ts +1 -1
  95. package/dist/ui/index.js +12 -12
  96. package/dist/ui.css +291 -48
  97. package/package.json +1 -1
  98. package/dist/ConfirmDialog-MTC7QMUJ.js +0 -4
  99. package/dist/PdfViewer-M2YGZIYX.js +0 -5
  100. package/dist/chunk-2GZQ352J.js.map +0 -1
  101. package/dist/chunk-3L463T5H.js.map +0 -1
  102. package/dist/chunk-7IN7YZAL.js.map +0 -1
  103. package/dist/chunk-BFBWLCO6.js.map +0 -1
  104. package/dist/chunk-BJ34QOR3.js.map +0 -1
  105. package/dist/chunk-CT3HVZRZ.js.map +0 -1
  106. package/dist/chunk-I464GYJJ.js.map +0 -1
  107. package/dist/chunk-LQTVTOSY.js.map +0 -1
  108. package/dist/chunk-SEWF4O3H.js.map +0 -1
  109. package/dist/chunk-UHPWZ2F4.js +0 -7
  110. package/dist/chunk-XH3ELPQZ.js.map +0 -1
  111. package/dist/chunk-ZKHD4J73.js.map +0 -1
package/dist/ui.css CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
- * react-os-shell/ui.css — styles for the UI kit, without the window chrome.
2
+ * efficient-shell/ui.css — styles for the UI kit, without the window chrome.
3
3
  *
4
- * Import this when you take `react-os-shell/ui`:
4
+ * Import this when you take `efficient-shell/ui`:
5
5
  *
6
- * import "react-os-shell/ui.css";
6
+ * import "efficient-shell/ui.css";
7
7
  *
8
- * Import EXACTLY ONE of this and `react-os-shell/styles.css` — the latter is
8
+ * Import EXACTLY ONE of this and `efficient-shell/styles.css` — the latter is
9
9
  * the umbrella over this file plus shell.css, so taking both doubles every rule.
10
10
  *
11
11
  * You supply Tailwind v4 yourself (`@import "tailwindcss"` in your own entry,
@@ -88,7 +88,7 @@
88
88
 
89
89
  /* Tailwind v4 doesn't scan node_modules by default — tell it to scan this
90
90
  package's compiled JS so utility classes used in kit components get
91
- generated when consumers import react-os-shell/ui.css. */
91
+ generated when consumers import efficient-shell/ui.css. */
92
92
  @source "./**/*.js";
93
93
 
94
94
  /* Tailwind scans every text file it can reach, prose included, so a class
@@ -153,7 +153,7 @@ html.rosh-reduce-transparency *::after {
153
153
  * `[data-theme="dark"]` scope read any of these twelve. It exists so a surface
154
154
  * built WITHOUT the kit's utility classes — a report, an emailed document, a
155
155
  * standalone page — can read the same roles the portals do. That surface is
156
- * governed by harness 52-brand-surface.md and loads `react-os-shell/brand.css`.
156
+ * governed by harness 52-brand-surface.md and loads `efficient-shell/brand.css`.
157
157
  *
158
158
  * They are load-bearing in light now: the timeline card is drawn from them in
159
159
  * both themes, and the frosted glass is tinted from `--surface` (through
@@ -580,7 +580,7 @@ html.rosh-reduce-transparency *::after {
580
580
  /* ── Kit colours ────────────────────────────────────────────────────────────
581
581
  * Every colour a kit component used to write as a literal in its TypeScript,
582
582
  * as a token a host can reach. `tests/kitColourLiterals.test.ts` walks
583
- * everything `react-os-shell/ui` reaches and fails on a hex, `rgb()`, `hsl()` or
583
+ * everything `efficient-shell/ui` reaches and fails on a hex, `rgb()`, `hsl()` or
584
584
  * any other colour function written into a string; the allow-list beside it
585
585
  * names the few literals that are data rather than chrome.
586
586
  *
@@ -681,6 +681,231 @@ html.rosh-reduce-transparency *::after {
681
681
  );
682
682
  }
683
683
 
684
+ /* ── DataTable on a narrow container ───────────────────────────────────────
685
+ * `<DataTable stackOnNarrow>`. Below 40rem of the TABLE'S OWN width every row
686
+ * is drawn as a card: one line per cell, the column's name on the left in the
687
+ * header's small uppercase voice, the value beside it. At 375px a strip of six
688
+ * columns wraps every cell onto two or three lines and pushes the last columns
689
+ * off the edge; a card gives each value the width there is.
690
+ *
691
+ * A CONTAINER query, not a media query. A table in a sidebar or a split pane
692
+ * on a wide monitor is as narrow as one on a phone, and only the container
693
+ * knows it. The wrapper (`rosh-dt-stack`) is the container, named so a table
694
+ * inside another container — a card that is one itself — answers to its own.
695
+ * 40rem rather than 640px so it moves with the reader's text size, as the
696
+ * columns it is making room for do. A container condition cannot read a custom
697
+ * property, which is why the width is not a token: one per table would mean a
698
+ * copy of every rule below for each.
699
+ *
700
+ * NOTHING HERE IS A DARK RULE. Every colour reads the neutral ramp, which the
701
+ * dark theme swaps underneath, so both themes are one set of rules.
702
+ *
703
+ * Why some declarations carry `!important`, and only those:
704
+ * - three are inline styles the component writes for the wide layout — a
705
+ * header's `width`, a pinned column's `left`, the table's `minWidth` —
706
+ * and an inline style beats any rule that is not important;
707
+ * - the rest are colours the dark remaps also set with `!important`
708
+ * (`.bg-white`, `.bg-gray-50`, `.border-gray-100` on the row). Those
709
+ * selectors weigh (0,2,0), so the rules that must beat them are written
710
+ * from `.rosh-dt-table` down to weigh more.
711
+ * Everything else is unlayered, which already beats the Tailwind utilities the
712
+ * wide layout is drawn with (they live in `@layer utilities`). A caller's row
713
+ * TINT survives: the card sets no background, so `rowClassName="bg-red-50"`
714
+ * still paints it. Its borders and spacing are the card's.
715
+ *
716
+ * The label is `::before { content: attr(data-label) }` with an EMPTY
717
+ * alternative text (`/ ""`). Generated content is otherwise part of a cell's
718
+ * accessible name, so a screen reader would hear "Qty, Qty 5": once from the
719
+ * column header it announces with every cell, once from the label. The first
720
+ * `content` is the fallback for an engine that cannot parse the second.
721
+ *
722
+ * Outside the query, `rosh-dt-value` is `display: contents`: the box the value
723
+ * is wrapped in so a card line is always two items however many nodes `render`
724
+ * returns, and in the wide layout no box at all.
725
+ */
726
+ .rosh-dt-stack {
727
+ container: rosh-dt / inline-size;
728
+ }
729
+ .rosh-dt-value {
730
+ display: contents;
731
+ }
732
+
733
+ @container rosh-dt (width < 40rem) {
734
+ /* The frame `bordered` draws steps aside: every card has an edge of its own,
735
+ and a frame around cards is a second border around each of them. */
736
+ .rosh-dt-scroll {
737
+ border-width: 0;
738
+ border-radius: 0;
739
+ }
740
+ .rosh-dt-table {
741
+ display: block;
742
+ min-width: 0 !important;
743
+ }
744
+
745
+ /* The header stays, compact. Sorting has to remain reachable, and it is
746
+ these buttons — with their arrows and their `aria-sort` — that do it, so
747
+ the sortable headers become a wrapping row of chips above the cards, and
748
+ "select all" leads it, above the column of boxes it controls. */
749
+ /* A grouped table has two header rows — "select all" in the first, the
750
+ grouped columns' chips in the second — so the rows sit side by side as
751
+ well as their chips, and the strip reads as one line wherever it fits.
752
+ Spacing is each chip's margin, not a gap, so a row left with nothing to
753
+ show takes no room at all. */
754
+ .rosh-dt-head {
755
+ display: flex;
756
+ flex-wrap: wrap;
757
+ align-items: center;
758
+ }
759
+ .rosh-dt-table > .rosh-dt-head > tr {
760
+ display: flex;
761
+ flex-wrap: wrap;
762
+ align-items: center;
763
+ border-width: 0;
764
+ background-color: transparent !important;
765
+ }
766
+ .rosh-dt-head th.rosh-dt-sort {
767
+ position: static;
768
+ width: auto !important;
769
+ margin: 0 0.375rem 0.5rem 0;
770
+ padding: 0;
771
+ border: 1px solid var(--line);
772
+ border-radius: var(--radius-md, 0.375rem);
773
+ /* The wide header's own fill, which is what the dark remap already gives
774
+ a pinned header — so neither theme needs to fight it. */
775
+ background-color: var(--surface-sunken);
776
+ }
777
+ .rosh-dt-head th.rosh-dt-sort[aria-sort="ascending"],
778
+ .rosh-dt-head th.rosh-dt-sort[aria-sort="descending"] {
779
+ border-color: var(--line-strong);
780
+ }
781
+ /* The button fills its chip, so the whole chip is the target — 32px tall,
782
+ past WCAG 2.5.8's 24. */
783
+ .rosh-dt-sort > button {
784
+ min-height: 2rem;
785
+ padding: 0.375rem 0.625rem;
786
+ }
787
+ /* A chip names its column group too: two chips both reading "Amount" would
788
+ leave the reader to guess which one sorts the debits. Hidden from the
789
+ accessibility tree like the card labels — the group header is still a
790
+ header, and is read with it. */
791
+ .rosh-dt-sort > button[data-group]::before {
792
+ content: attr(data-group) "\a0\b7\a0";
793
+ content: attr(data-group) "\a0\b7\a0" / "";
794
+ }
795
+ .rosh-dt-table > .rosh-dt-head > tr > th.rosh-dt-select {
796
+ position: static;
797
+ width: auto !important;
798
+ margin: 0 0.375rem 0.5rem 0;
799
+ /* Level with the card checkboxes below: a card's 1px edge, then its
800
+ padding. */
801
+ padding: 0.25rem 0.5rem 0.25rem calc(0.75rem + 1px);
802
+ background-color: transparent !important;
803
+ }
804
+ /* Every other header — a column that does not sort, a group — leaves the
805
+ screen but not the table: a screen reader still finds each cell's column
806
+ header. `left` is reset because a pinned header carries an inline offset,
807
+ and a 1px box parked 400px to the right is horizontal overflow. */
808
+ .rosh-dt-head th:not(.rosh-dt-sort, .rosh-dt-select) {
809
+ position: absolute;
810
+ top: 0 !important;
811
+ left: 0 !important;
812
+ width: 1px !important;
813
+ height: 1px;
814
+ margin: -1px;
815
+ padding: 0;
816
+ overflow: hidden;
817
+ clip-path: inset(50%);
818
+ white-space: nowrap;
819
+ border-width: 0;
820
+ }
821
+
822
+ /* One card per row. */
823
+ .rosh-dt-body {
824
+ display: grid;
825
+ gap: 0.5rem;
826
+ }
827
+ .rosh-dt-table > .rosh-dt-body > .rosh-dt-row {
828
+ display: block;
829
+ position: relative;
830
+ padding: 0.25rem 0.75rem;
831
+ border: 1px solid var(--line) !important;
832
+ border-radius: var(--radius-lg, 0.5rem);
833
+ }
834
+ /* The checkbox sits in the card's top-left corner, level with the first
835
+ line, rather than spending a line of its own. */
836
+ .rosh-dt-table > .rosh-dt-body > .rosh-dt-row-select {
837
+ padding-left: 2.5rem;
838
+ }
839
+ .rosh-dt-table > .rosh-dt-body > .rosh-dt-row > td.rosh-dt-select {
840
+ position: absolute;
841
+ top: 0;
842
+ left: 0 !important;
843
+ display: flex;
844
+ width: auto !important;
845
+ padding: 0.75rem;
846
+ background-color: transparent !important;
847
+ }
848
+
849
+ /* One line per cell: the label, then the value. A pinned cell's background
850
+ was there to hide the columns scrolling under it; there are none. */
851
+ .rosh-dt-table > .rosh-dt-body > .rosh-dt-row > td.rosh-dt-cell {
852
+ display: block;
853
+ position: static;
854
+ max-width: none;
855
+ padding: 0.375rem 0;
856
+ background-color: transparent !important;
857
+ }
858
+ .rosh-dt-cell + .rosh-dt-cell {
859
+ border-top: 1px solid var(--line-subtle);
860
+ }
861
+ .rosh-dt-table > .rosh-dt-body > .rosh-dt-row > td.rosh-dt-cell[data-label] {
862
+ display: grid;
863
+ grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
864
+ column-gap: 0.75rem;
865
+ align-items: baseline;
866
+ }
867
+ .rosh-dt-cell[data-label]::before {
868
+ content: attr(data-label);
869
+ content: attr(data-label) / "";
870
+ font-size: 0.75rem;
871
+ line-height: 1rem;
872
+ font-weight: 600;
873
+ letter-spacing: 0.025em;
874
+ text-transform: uppercase;
875
+ /* Left whatever the column's alignment: that is the VALUE's. And free to
876
+ wrap in an `ellipsis` column, whose cell is `nowrap` for the value. */
877
+ text-align: start;
878
+ white-space: normal;
879
+ color: var(--ink-faint);
880
+ overflow-wrap: anywhere;
881
+ }
882
+ /* The value keeps the cell's alignment, so a figures column still lines its
883
+ decimal points up at the card's right edge. A long token — an email, a
884
+ part number — breaks rather than pushing the card wider than the screen. */
885
+ .rosh-dt-cell > .rosh-dt-value {
886
+ display: block;
887
+ min-width: 0;
888
+ overflow-wrap: anywhere;
889
+ }
890
+ /* `ellipsis` keeps its promise of one line; the cell is a grid now, and
891
+ only the value can carry the ellipsis. */
892
+ .rosh-dt-clip > .rosh-dt-value {
893
+ overflow: hidden;
894
+ text-overflow: ellipsis;
895
+ white-space: nowrap;
896
+ }
897
+
898
+ .rosh-dt-body > .rosh-dt-empty,
899
+ .rosh-dt-body > .rosh-dt-empty > td {
900
+ display: block;
901
+ }
902
+
903
+ /* The pager wraps rather than running off a narrow screen. */
904
+ .rosh-dt-pager > nav {
905
+ flex-wrap: wrap;
906
+ }
907
+ }
908
+
684
909
  /* ── Chart motion ───────────────────────────────────────────────────────────
685
910
  * Every animation the chart family uses, as classes, so no chart implements
686
911
  * motion itself and none of them can forget the reduced-motion rule below.
@@ -1370,30 +1595,48 @@ html.rosh-reduce-transparency *::after {
1370
1595
  :is([data-theme="dark"], .dark) .hover\:bg-gray-100:hover { background-color: var(--surface-raised) !important; }
1371
1596
  :is([data-theme="dark"], .dark) .hover\:bg-gray-200:hover { background-color: var(--line-strong) !important; }
1372
1597
 
1598
+ /* ── The accent, in dark ───────────────────────────────────────────────────
1599
+ * The kit's accent is Tailwind's blue. In light it is painted with the blue
1600
+ * utilities themselves, so a host that maps `--color-blue-*` onto its own accent
1601
+ * recolours it. In dark these remaps paint it instead, and they used to write
1602
+ * blue as literals, so a host's accent turned blue at night. They read these four
1603
+ * now. Channels rather than colours, because most of them are washes at an alpha
1604
+ * of their own (`rgba(var(--rosh-dark-accent-rgb), 0.26)`).
1605
+ *
1606
+ * Every default is the literal it replaced (blue-500 / blue-400 / blue-300 /
1607
+ * blue-200), so nothing renders differently for a host that sets none.
1608
+ * `tests/darkAccentTokens.test.ts` holds every dark blue rule to them. */
1609
+ :root {
1610
+ --rosh-dark-accent-rgb: 59, 130, 246;
1611
+ --rosh-dark-accent-line-rgb: 96, 165, 250;
1612
+ --rosh-dark-accent-ink-rgb: 147, 197, 253;
1613
+ --rosh-dark-accent-ink-strong-rgb: 191, 219, 254;
1614
+ }
1615
+
1373
1616
  /* Blue hover states — used by menu items, profile row, etc.
1374
1617
  In dark mode swap the near-white bg for a dark blue tint and the dark
1375
1618
  blue text for a light shade so highlighted items stay readable. */
1376
1619
  :is([data-theme="dark"], .dark) .bg-blue-50,
1377
- :is([data-theme="dark"], .dark) .hover\:bg-blue-50:hover { background-color: rgba(59, 130, 246, 0.26) !important; }
1620
+ :is([data-theme="dark"], .dark) .hover\:bg-blue-50:hover { background-color: rgba(var(--rosh-dark-accent-rgb), 0.26) !important; }
1378
1621
  :is([data-theme="dark"], .dark) .text-blue-700,
1379
- :is([data-theme="dark"], .dark) .hover\:text-blue-700:hover { color: #bfdbfe !important; }
1380
- :is([data-theme="dark"], .dark) .text-blue-600 { color: #93c5fd !important; }
1381
- :is([data-theme="dark"], .dark) .text-blue-800 { color: #bfdbfe !important; }
1622
+ :is([data-theme="dark"], .dark) .hover\:text-blue-700:hover { color: rgb(var(--rosh-dark-accent-ink-strong-rgb)) !important; }
1623
+ :is([data-theme="dark"], .dark) .text-blue-600 { color: rgb(var(--rosh-dark-accent-ink-rgb)) !important; }
1624
+ :is([data-theme="dark"], .dark) .text-blue-800 { color: rgb(var(--rosh-dark-accent-ink-strong-rgb)) !important; }
1382
1625
 
1383
1626
  /* Bare *-100 highlight backgrounds + *-700 text (status pills, badges, avatars,
1384
1627
  dialog icons) have no dark override otherwise, so they keep their bright
1385
1628
  light-mode value and wash out the text on top. Mute the background to a low
1386
1629
  tint and lighten the text so highlighted content stays readable in dark mode. */
1387
- :is([data-theme="dark"], .dark) .bg-blue-100 { background-color: rgba(59, 130, 246, 0.22) !important; }
1630
+ :is([data-theme="dark"], .dark) .bg-blue-100 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.22) !important; }
1388
1631
 
1389
1632
  /* Tailwind's `!` important modifier compiles to distinct class names
1390
1633
  (`.\!bg-blue-100` etc.), so the EditableGrid selection utilities escape the
1391
1634
  remaps above: the selection keeps its light-mode background while the cell
1392
1635
  text goes light, making selected cells unreadable in dark mode. The
1393
1636
  attribute selector outranks the utility's own !important. */
1394
- :is([data-theme="dark"], .dark) .\!bg-blue-50 { background-color: rgba(59, 130, 246, 0.26) !important; }
1395
- :is([data-theme="dark"], .dark) .\!bg-blue-100 { background-color: rgba(59, 130, 246, 0.22) !important; }
1396
- :is([data-theme="dark"], .dark) .\!bg-blue-200 { background-color: rgba(59, 130, 246, 0.35) !important; }
1637
+ :is([data-theme="dark"], .dark) .\!bg-blue-50 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.26) !important; }
1638
+ :is([data-theme="dark"], .dark) .\!bg-blue-100 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.22) !important; }
1639
+ :is([data-theme="dark"], .dark) .\!bg-blue-200 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.35) !important; }
1397
1640
  :is([data-theme="dark"], .dark) .\!text-gray-700 { color: var(--ink) !important; }
1398
1641
  :is([data-theme="dark"], .dark) .bg-green-100 { background-color: rgba(34, 197, 94, 0.18) !important; }
1399
1642
  :is([data-theme="dark"], .dark) .bg-emerald-100 { background-color: rgba(16, 185, 129, 0.18) !important; }
@@ -1437,16 +1680,16 @@ html.rosh-reduce-transparency *::after {
1437
1680
  :is([data-theme="dark"], .dark) .bg-gray-100\/60 { background-color: rgba(30, 30, 46, 0.6) !important; }
1438
1681
  :is([data-theme="dark"], .dark) .bg-gray-200\/40 { background-color: rgba(49, 50, 68, 0.4) !important; }
1439
1682
  :is([data-theme="dark"], .dark) .bg-gray-200\/50 { background-color: rgba(49, 50, 68, 0.5) !important; }
1440
- :is([data-theme="dark"], .dark) .bg-blue-100\/60 { background-color: rgba(59, 130, 246, 0.22) !important; }
1683
+ :is([data-theme="dark"], .dark) .bg-blue-100\/60 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.22) !important; }
1441
1684
  /* Pale-blue tinted panels (selected rows, inline-reply box) — scale the
1442
1685
  .bg-blue-50 dark tint (0.26) down through the alpha steps. */
1443
- :is([data-theme="dark"], .dark) .bg-blue-50\/30 { background-color: rgba(59, 130, 246, 0.10) !important; }
1444
- :is([data-theme="dark"], .dark) .bg-blue-50\/40 { background-color: rgba(59, 130, 246, 0.12) !important; }
1445
- :is([data-theme="dark"], .dark) .bg-blue-50\/50 { background-color: rgba(59, 130, 246, 0.15) !important; }
1446
- :is([data-theme="dark"], .dark) .bg-blue-50\/60 { background-color: rgba(59, 130, 246, 0.18) !important; }
1686
+ :is([data-theme="dark"], .dark) .bg-blue-50\/30 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.10) !important; }
1687
+ :is([data-theme="dark"], .dark) .bg-blue-50\/40 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.12) !important; }
1688
+ :is([data-theme="dark"], .dark) .bg-blue-50\/50 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.15) !important; }
1689
+ :is([data-theme="dark"], .dark) .bg-blue-50\/60 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.18) !important; }
1447
1690
  :is([data-theme="dark"], .dark) .border-gray-200\/40 { border-color: rgba(69, 71, 90, 0.4) !important; }
1448
1691
  :is([data-theme="dark"], .dark) .border-gray-300\/40 { border-color: rgba(88, 91, 112, 0.4) !important; }
1449
- :is([data-theme="dark"], .dark) .border-blue-400\/60 { border-color: rgba(96, 165, 250, 0.45) !important; }
1692
+ :is([data-theme="dark"], .dark) .border-blue-400\/60 { border-color: rgba(var(--rosh-dark-accent-line-rgb), 0.45) !important; }
1450
1693
  :is([data-theme="dark"], .dark) .hover\:bg-gray-200\/40:hover { background-color: rgba(49, 50, 68, 0.4) !important; }
1451
1694
 
1452
1695
  /* White alpha variants — the bare .bg-white override doesn't match the /50../95
@@ -1459,7 +1702,7 @@ html.rosh-reduce-transparency *::after {
1459
1702
  :is([data-theme="dark"], .dark) .bg-white\/80 { background-color: rgba(30, 30, 46, 0.85) !important; }
1460
1703
  :is([data-theme="dark"], .dark) .bg-white\/90 { background-color: rgba(30, 30, 46, 0.92) !important; }
1461
1704
  :is([data-theme="dark"], .dark) .bg-white\/95 { background-color: rgba(30, 30, 46, 0.95) !important; }
1462
- :is([data-theme="dark"], .dark) .bg-blue-50\/70 { background-color: rgba(59, 130, 246, 0.22) !important; }
1705
+ :is([data-theme="dark"], .dark) .bg-blue-50\/70 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.22) !important; }
1463
1706
 
1464
1707
  /* ── Bare elements ─────────────────────────────────────────────────────────
1465
1708
  The next three rules style elements by TAG — every input, select and
@@ -1506,7 +1749,7 @@ html.rosh-reduce-transparency *::after {
1506
1749
  :is([data-theme="dark"], .dark) .bg-gray-100.text-gray-800 { background-color: var(--surface-raised) !important; color: var(--ink) !important; }
1507
1750
  :is([data-theme="dark"], .dark) .bg-gray-300.text-gray-800 { background-color: var(--line-strong) !important; color: var(--ink-strongest) !important; }
1508
1751
  :is([data-theme="dark"], .dark) .bg-green-100.text-green-800 { background-color: rgba(34, 197, 94, 0.18) !important; color: #86efac !important; }
1509
- :is([data-theme="dark"], .dark) .bg-blue-100.text-blue-800 { background-color: rgba(59, 130, 246, 0.18) !important; color: #93c5fd !important; }
1752
+ :is([data-theme="dark"], .dark) .bg-blue-100.text-blue-800 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.18) !important; color: rgb(var(--rosh-dark-accent-ink-rgb)) !important; }
1510
1753
  :is([data-theme="dark"], .dark) .bg-indigo-100.text-indigo-800 { background-color: rgba(99, 102, 241, 0.18) !important; color: #a5b4fc !important; }
1511
1754
  :is([data-theme="dark"], .dark) .bg-sky-100.text-sky-800 { background-color: rgba(14, 165, 233, 0.18) !important; color: #7dd3fc !important; }
1512
1755
  :is([data-theme="dark"], .dark) .bg-yellow-100.text-yellow-800 { background-color: rgba(234, 179, 8, 0.18) !important; color: #fde68a !important; }
@@ -1656,27 +1899,27 @@ html.rosh-reduce-transparency *::after {
1656
1899
  /* Blue — gaps beyond the accent remaps above. Hover/active steps sit one
1657
1900
  ladder step above their resting tint (base .26 → active .30; the /30../50
1658
1901
  alpha ladder steps 0.10/0.12/0.15 → hovers 0.12/0.15/0.18). */
1659
- :is([data-theme="dark"], .dark) .bg-blue-200 { background-color: rgba(59, 130, 246, 0.35) !important; }
1660
- :is([data-theme="dark"], .dark) .active\:bg-blue-200:active { background-color: rgba(59, 130, 246, 0.40) !important; }
1661
- :is([data-theme="dark"], .dark) .bg-blue-200\/60 { background-color: rgba(59, 130, 246, 0.20) !important; }
1662
- :is([data-theme="dark"], .dark) .hover\:bg-blue-100:hover { background-color: rgba(59, 130, 246, 0.26) !important; }
1663
- :is([data-theme="dark"], .dark) .active\:bg-blue-50:active { background-color: rgba(59, 130, 246, 0.30) !important; }
1664
- :is([data-theme="dark"], .dark) .hover\:bg-blue-50\/30:hover { background-color: rgba(59, 130, 246, 0.12) !important; }
1665
- :is([data-theme="dark"], .dark) .hover\:bg-blue-50\/40:hover { background-color: rgba(59, 130, 246, 0.15) !important; }
1666
- :is([data-theme="dark"], .dark) .hover\:bg-blue-50\/50:hover { background-color: rgba(59, 130, 246, 0.18) !important; }
1667
- :is([data-theme="dark"], .dark) .border-blue-100 { border-color: rgba(59, 130, 246, 0.22) !important; }
1902
+ :is([data-theme="dark"], .dark) .bg-blue-200 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.35) !important; }
1903
+ :is([data-theme="dark"], .dark) .active\:bg-blue-200:active { background-color: rgba(var(--rosh-dark-accent-rgb), 0.40) !important; }
1904
+ :is([data-theme="dark"], .dark) .bg-blue-200\/60 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.20) !important; }
1905
+ :is([data-theme="dark"], .dark) .hover\:bg-blue-100:hover { background-color: rgba(var(--rosh-dark-accent-rgb), 0.26) !important; }
1906
+ :is([data-theme="dark"], .dark) .active\:bg-blue-50:active { background-color: rgba(var(--rosh-dark-accent-rgb), 0.30) !important; }
1907
+ :is([data-theme="dark"], .dark) .hover\:bg-blue-50\/30:hover { background-color: rgba(var(--rosh-dark-accent-rgb), 0.12) !important; }
1908
+ :is([data-theme="dark"], .dark) .hover\:bg-blue-50\/40:hover { background-color: rgba(var(--rosh-dark-accent-rgb), 0.15) !important; }
1909
+ :is([data-theme="dark"], .dark) .hover\:bg-blue-50\/50:hover { background-color: rgba(var(--rosh-dark-accent-rgb), 0.18) !important; }
1910
+ :is([data-theme="dark"], .dark) .border-blue-100 { border-color: rgba(var(--rosh-dark-accent-rgb), 0.22) !important; }
1668
1911
  :is([data-theme="dark"], .dark) .border-blue-200,
1669
- :is([data-theme="dark"], .dark) .hover\:border-blue-200:hover { border-color: rgba(59, 130, 246, 0.32) !important; }
1912
+ :is([data-theme="dark"], .dark) .hover\:border-blue-200:hover { border-color: rgba(var(--rosh-dark-accent-rgb), 0.32) !important; }
1670
1913
  :is([data-theme="dark"], .dark) .border-blue-300,
1671
- :is([data-theme="dark"], .dark) .hover\:border-blue-300:hover { border-color: rgba(59, 130, 246, 0.45) !important; }
1672
- :is([data-theme="dark"], .dark) .border-blue-300\/50 { border-color: rgba(59, 130, 246, 0.25) !important; }
1673
- :is([data-theme="dark"], .dark) .text-blue-900 { color: #bfdbfe !important; }
1674
- :is([data-theme="dark"], .dark) .hover\:text-blue-600:hover { color: #93c5fd !important; }
1675
- :is([data-theme="dark"], .dark) .hover\:text-blue-800:hover { color: #bfdbfe !important; }
1914
+ :is([data-theme="dark"], .dark) .hover\:border-blue-300:hover { border-color: rgba(var(--rosh-dark-accent-rgb), 0.45) !important; }
1915
+ :is([data-theme="dark"], .dark) .border-blue-300\/50 { border-color: rgba(var(--rosh-dark-accent-rgb), 0.25) !important; }
1916
+ :is([data-theme="dark"], .dark) .text-blue-900 { color: rgb(var(--rosh-dark-accent-ink-strong-rgb)) !important; }
1917
+ :is([data-theme="dark"], .dark) .hover\:text-blue-600:hover { color: rgb(var(--rosh-dark-accent-ink-rgb)) !important; }
1918
+ :is([data-theme="dark"], .dark) .hover\:text-blue-800:hover { color: rgb(var(--rosh-dark-accent-ink-strong-rgb)) !important; }
1676
1919
  /* File-input buttons (file: compiles to ::file-selector-button) */
1677
- :is([data-theme="dark"], .dark) .file\:bg-blue-50::file-selector-button { background-color: rgba(59, 130, 246, 0.26) !important; }
1678
- :is([data-theme="dark"], .dark) .file\:text-blue-700::file-selector-button { color: #bfdbfe !important; }
1679
- :is([data-theme="dark"], .dark) .hover\:file\:bg-blue-100:hover::file-selector-button { background-color: rgba(59, 130, 246, 0.26) !important; }
1920
+ :is([data-theme="dark"], .dark) .file\:bg-blue-50::file-selector-button { background-color: rgba(var(--rosh-dark-accent-rgb), 0.26) !important; }
1921
+ :is([data-theme="dark"], .dark) .file\:text-blue-700::file-selector-button { color: rgb(var(--rosh-dark-accent-ink-strong-rgb)) !important; }
1922
+ :is([data-theme="dark"], .dark) .hover\:file\:bg-blue-100:hover::file-selector-button { background-color: rgba(var(--rosh-dark-accent-rgb), 0.26) !important; }
1680
1923
 
1681
1924
  /* Gray interaction-state variants (separate class names from the bare remaps) */
1682
1925
  :is([data-theme="dark"], .dark) .hover\:text-gray-600:hover,
@@ -1706,7 +1949,7 @@ html.rosh-reduce-transparency *::after {
1706
1949
  :is([data-theme="dark"], .dark) .text-black { color: var(--ink-strongest) !important; }
1707
1950
  :is([data-theme="dark"], .dark) .hover\:text-black:hover { color: #ffffff !important; }
1708
1951
 
1709
- /* Markdown — the clamped note (react-os-shell/markdown, `clamp`).
1952
+ /* Markdown — the clamped note (efficient-shell/markdown, `clamp`).
1710
1953
 
1711
1954
  The fade MASKS the text rather than laying a coloured gradient over it. A
1712
1955
  gradient has to know what colour to fade to, and there is no answer that
@@ -1896,12 +2139,12 @@ html.rosh-reduce-transparency *::after {
1896
2139
  :is([data-theme="dark"], .dark) .hover\:bg-gray-200\/70:hover { background-color: rgba(49, 50, 68, 0.7) !important; }
1897
2140
  :is([data-theme="dark"], .dark) .hover\:bg-gray-200\/90:hover { background-color: rgba(49, 50, 68, 0.9) !important; }
1898
2141
  :is([data-theme="dark"], .dark) .border-gray-200\/60 { border-color: rgba(69, 71, 90, 0.6) !important; }
1899
- :is([data-theme="dark"], .dark) .bg-blue-50\/80 { background-color: rgba(59, 130, 246, 0.24) !important; }
1900
- :is([data-theme="dark"], .dark) .bg-blue-50\/90 { background-color: rgba(59, 130, 246, 0.26) !important; }
1901
- :is([data-theme="dark"], .dark) .text-blue-600\/70 { color: rgba(147, 197, 253, 0.7) !important; }
1902
- :is([data-theme="dark"], .dark) .text-blue-700\/70 { color: rgba(191, 219, 254, 0.7) !important; }
1903
- :is([data-theme="dark"], .dark) .text-blue-800\/80 { color: rgba(191, 219, 254, 0.8) !important; }
1904
- :is([data-theme="dark"], .dark) .text-blue-900\/60 { color: rgba(191, 219, 254, 0.6) !important; }
2142
+ :is([data-theme="dark"], .dark) .bg-blue-50\/80 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.24) !important; }
2143
+ :is([data-theme="dark"], .dark) .bg-blue-50\/90 { background-color: rgba(var(--rosh-dark-accent-rgb), 0.26) !important; }
2144
+ :is([data-theme="dark"], .dark) .text-blue-600\/70 { color: rgba(var(--rosh-dark-accent-ink-rgb), 0.7) !important; }
2145
+ :is([data-theme="dark"], .dark) .text-blue-700\/70 { color: rgba(var(--rosh-dark-accent-ink-strong-rgb), 0.7) !important; }
2146
+ :is([data-theme="dark"], .dark) .text-blue-800\/80 { color: rgba(var(--rosh-dark-accent-ink-strong-rgb), 0.8) !important; }
2147
+ :is([data-theme="dark"], .dark) .text-blue-900\/60 { color: rgba(var(--rosh-dark-accent-ink-strong-rgb), 0.6) !important; }
1905
2148
  :is([data-theme="dark"], .dark) .bg-amber-200\/70 { background-color: rgba(245, 158, 11, 0.20) !important; }
1906
2149
  :is([data-theme="dark"], .dark) .hover\:bg-amber-50\/70:hover { background-color: rgba(245, 158, 11, 0.12) !important; }
1907
2150
  :is([data-theme="dark"], .dark) .text-amber-800\/80 { color: rgba(253, 230, 138, 0.8) !important; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "efficient-shell",
3
- "version": "4.131.0",
3
+ "version": "4.134.0",
4
4
  "description": "Desktop-style React UI shell — windows, taskbar, start menu, sticky notes, frosted glass theming, and bundled apps.",
5
5
  "license": "MIT",
6
6
  "author": "Victor Y. Mau",
@@ -1,4 +0,0 @@
1
- export { ConfirmProvider, confirm, confirmDestructive, prompt, useConfirm } from './chunk-XH3ELPQZ.js';
2
- import './chunk-7IN7YZAL.js';
3
- //# sourceMappingURL=ConfirmDialog-MTC7QMUJ.js.map
4
- //# sourceMappingURL=ConfirmDialog-MTC7QMUJ.js.map
@@ -1,5 +0,0 @@
1
- export { PdfViewer as default, fitScale } from './chunk-SEWF4O3H.js';
2
- import './chunk-EENVXIVU.js';
3
- import './chunk-7IN7YZAL.js';
4
- //# sourceMappingURL=PdfViewer-M2YGZIYX.js.map
5
- //# sourceMappingURL=PdfViewer-M2YGZIYX.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/apps/_about.tsx"],"names":[],"mappings":"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","file":"chunk-2GZQ352J.js","sourcesContent":["/**\n * Shared \"About\" dialog for the bundled apps.\n *\n * Each bundled app carries its own version (independent of the package\n * version) so app-level changes can be tracked without bumping every app at\n * once. `<AboutApp app=\"…\" />` registers an \"About <Name>\" item in the\n * window title menu and renders the dialog: app icon, name, app version and\n * the react-os-shell attribution + shell version.\n *\n * Bump an app's version in `BUILTIN_APP_INFO` whenever that app's behaviour\n * changes, alongside the package-level CHANGELOG entry.\n */\nimport { useCallback, useState, isValidElement, cloneElement, type ReactElement } from 'react';\nimport Modal, { useWindowMenuItem } from '../shell/Modal';\nimport { navIcons } from '../shell-config/nav';\nimport { VERSION } from '../version';\n\nexport interface BuiltinAppInfo {\n /** Display name — matches the window-registry label. */\n name: string;\n /** App version, independent of the package version. */\n version: string;\n /** One-line summary shown in the About dialog. */\n description: string;\n /** Registry route — used to look up the consumer-registered nav icon. */\n route: string;\n}\n\nexport const BUILTIN_APP_INFO = {\n spreadsheet: {\n name: 'Spreadsheets',\n version: '1.1.3',\n route: '/spreadsheet',\n description: 'Multi-sheet spreadsheet editor with CSV / TSV import and export.',\n },\n notepad: {\n name: 'Notepad',\n version: '1.0.0',\n route: '/notepad',\n description: 'Color-coded notes with checklists, desktop stickies and entity autolinking.',\n },\n documents: {\n name: 'Documents',\n version: '1.1.0',\n route: '/documents',\n description: 'Letter-page editor for Word documents and plain-text files — images, lists, alignment.',\n },\n preview: {\n name: 'Preview',\n version: '1.3.2',\n route: '/preview',\n description: 'Viewer for PDF documents, images, DXF drawings and 3D models — CAD-style measuring with object snap and an AutoCAD-like command bar.',\n },\n files: {\n name: 'Files',\n version: '1.1.0',\n route: '/files',\n description: 'Personal file manager with folders, uploads, quota, trash and desktop shortcut folders.',\n },\n browser: {\n name: 'Browser',\n version: '1.0.1',\n route: '/browser',\n description: 'Minimal web browser with bookmarks, history and navigation bar.',\n },\n} satisfies Record<string, BuiltinAppInfo>;\n\nexport type BuiltinAppId = keyof typeof BUILTIN_APP_INFO;\n\nconst ABOUT_MENU_ICON = (\n <svg className=\"h-4 w-4 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z\" />\n </svg>\n);\n\n// Generic window glyph shown when the consumer hasn't registered a nav icon\n// for the app's route (same fallback shape as the Modal title bar).\nconst FALLBACK_APP_ICON = (\n <svg className=\"h-9 w-9\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M3.75 6.75A2.25 2.25 0 016 4.5h12a2.25 2.25 0 012.25 2.25v10.5A2.25 2.25 0 0118 19.5H6a2.25 2.25 0 01-2.25-2.25V6.75z M3.75 9h16.5\" />\n </svg>\n);\n\n/** Registers \"About <Name>\" in the window title menu and renders the dialog.\n * Render anywhere inside the app's window. */\nexport default function AboutApp({ app }: { app: BuiltinAppId }) {\n const info = BUILTIN_APP_INFO[app];\n const [open, setOpen] = useState(false);\n const openDialog = useCallback(() => setOpen(true), []);\n useWindowMenuItem(`About ${info.name}`, openDialog, ABOUT_MENU_ICON);\n\n if (!open) return null;\n\n const registered = navIcons[info.route];\n const appIcon = isValidElement(registered)\n ? cloneElement(registered as ReactElement, { className: 'h-9 w-9' } as any)\n : FALLBACK_APP_ICON;\n\n return (\n <Modal open onClose={() => setOpen(false)} title={`About ${info.name}`} size=\"sm\" compact bodyScroll={false} autoHeight dimensions={[320, 320]}>\n <div className=\"flex flex-col items-center text-center px-5 pt-5 pb-4\">\n <div className=\"flex h-16 w-16 items-center justify-center rounded-2xl bg-gray-100 text-gray-600 shrink-0\">\n {appIcon}\n </div>\n <h2 className=\"mt-3 text-lg font-bold text-gray-900\">{info.name}</h2>\n <p className=\"text-[11px] font-mono text-gray-400\">Version {info.version}</p>\n <p className=\"mt-2 text-xs text-gray-500 leading-relaxed\">{info.description}</p>\n <div className=\"mt-4 pt-3 border-t border-gray-200 w-full\">\n <p className=\"text-[11px] text-gray-500\">\n Part of the <span className=\"font-medium text-gray-700\">react-os-shell</span> desktop environment\n </p>\n {VERSION && <p className=\"mt-0.5 text-[10px] font-mono text-gray-400\">shell v{VERSION}</p>}\n </div>\n </div>\n </Modal>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/apps/_previewStage.ts"],"names":[],"mappings":";AA+CO,IAAM,wBAAA,GAA2B;AAaxC,IAAI,OAAA,GAAkC,IAAA;AACtC,IAAI,SAAA,GAAY,CAAA;AAKT,SAAS,cAAc,IAAA,EAAwC;AACpE,EAAA,MAAM,QAAQ,EAAE,SAAA;AAChB,EAAA,MAAM,KAAA,GAAyB,EAAE,KAAA,EAAO,IAAA,EAAK;AAC7C,EAAA,OAAA,GAAU,KAAA;AACV,EAAA,OAAO;AAAA,IACL,OAAO,IAAA,EAAsB;AAY3B,MAAA,IAAI,OAAA,KAAY,KAAA,EAAO,KAAA,CAAM,IAAA,GAAO,IAAA;AACpC,MAAA,IAAI,OAAO,WAAW,WAAA,EAAa;AACnC,MAAA,MAAA,CAAO,aAAA,CAAc,IAAI,WAAA,CAAY,wBAAA,EAA0B,EAAE,MAAA,EAAQ,EAAE,KAAA,EAAO,IAAA,EAAM,IAAA,EAAK,EAAG,CAAC,CAAA;AAAA,IACnG;AAAA,GACF;AACF;AAGO,SAAS,mBAAA,GAA8C;AAC5D,EAAA,OAAO,OAAA;AACT;AAKO,SAAS,qBAAqB,KAAA,EAA8B;AACjE,EAAA,IAAI,OAAA,KAAY,OAAO,OAAA,GAAU,IAAA;AACnC","file":"chunk-3L463T5H.js","sourcesContent":["/**\n * PDF Preview staging — the tiny consumer-facing surface of the Preview app.\n *\n * Lives apart from Preview.tsx so importing `setPdfPreview` (from\n * `react-os-shell/apps` or shell internals like PdfActionButton /\n * openPreviewFile) never drags the viewer implementation — and its static\n * pdfjs-dist import — into a host's startup bundle. The Preview window\n * component stays behind its React.lazy dynamic import and drains the stage\n * on mount via the @internal peek/claim helpers below.\n */\n\nexport interface PdfPreviewData {\n /** Object URL or remote URL of the PDF. Blob URLs are revoked when the window unmounts.\n * Leave blank when staging a `converting: true` placeholder; call `handle.update({...})`\n * on the handle returned by `setPdfPreview` once conversion finishes. */\n url?: string;\n /** Display name (and download filename). */\n filename: string;\n /** Renderer to use. Defaults to `'pdf'`. `'dxf'` requires the consumer to\n * have `dxf-viewer` installed (it's an optional peer dep). `'image'`\n * renders an `<img>` for raster screenshots / photos. `'3d'` covers\n * STEP / STL / OBJ / GLTF / 3MF / IGES via the optional\n * `online-3d-viewer` peer dep. */\n kind?: 'pdf' | 'dxf' | 'image' | '3d';\n /** Optional download handler — replaces the built-in \"save URL as filename\" if supplied. */\n onDownload?: () => void;\n /** Optional email handler — only shown when supplied. */\n onEmail?: () => void;\n /** Show a progress placeholder while the consumer fetches/converts the file. */\n converting?: boolean;\n /** Headline shown on the converting placeholder (e.g. \"CONVERTING DWG FILE\"). */\n convertingMessage?: string;\n}\n\n/** Handle returned by `setPdfPreview`. Holds the identity of the staged\n * payload so a later `.update()` only targets the window that picked it up\n * (not every open Preview, which would clobber unrelated windows). */\nexport interface PdfPreviewHandle {\n /** Replace the data shown in the window that consumed this staging. Safe to\n * call before that window exists — an update that lands while the payload\n * is still staged replaces the staged payload, so the window opens on the\n * resolved file instead of the placeholder it was staged with.\n * No-op only once the window that consumed the staging has been closed. */\n update(next: PdfPreviewData): void;\n}\n\n/** @internal Event carrying `.update()` payloads to the claiming window. */\nexport const PDF_PREVIEW_UPDATE_EVENT = 'react-os-shell:pdf-preview-update';\n\n/** @internal Staged payload awaiting the next Preview window mount.\n *\n * `data` is MUTABLE and deliberately so: `update()` rewrites it in place\n * while the stage is still unclaimed. Restaging a fresh object instead would\n * break the identity that `claimPdfPreviewStage` and the claimant's own\n * `consumedRef` are holding. */\nexport interface PendingPdfStage {\n token: number;\n data: PdfPreviewData;\n}\n\nlet pending: PendingPdfStage | null = null;\nlet nextToken = 0;\n\n/** Stage a PDF for the next Preview window mount. The returned handle's\n * `update()` method swaps content in *that* specific window only — use it\n * to replace a `converting: true` placeholder with the resolved file. */\nexport function setPdfPreview(data: PdfPreviewData): PdfPreviewHandle {\n const token = ++nextToken;\n const stage: PendingPdfStage = { token, data };\n pending = stage;\n return {\n update(next: PdfPreviewData) {\n // The window is opened by `openPage('/preview')` and Preview is a lazy\n // chunk, so a consumer that resolves quickly — a cached PDF, a fast\n // endpoint, a second preview in the same session — can call this before\n // any window has mounted to listen. The event is fire-and-forget, so on\n // its own it would be dropped and the window would open on the\n // placeholder and sit there for ever. Rewrite the stage as well while it\n // is still ours: whoever drains it next gets the current payload.\n //\n // `pending === stage` is exactly \"no window has claimed this yet\".\n // Claiming and binding the listener happen in the same commit (see\n // Preview.tsx), so there is no third state where both paths miss.\n if (pending === stage) stage.data = next;\n if (typeof window === 'undefined') return;\n window.dispatchEvent(new CustomEvent(PDF_PREVIEW_UPDATE_EVENT, { detail: { token, data: next } }));\n },\n };\n}\n\n/** @internal Render-phase peek — see Preview.tsx for the drain protocol. */\nexport function peekPdfPreviewStage(): PendingPdfStage | null {\n return pending;\n}\n\n/** @internal Commit-phase claim. The identity check keeps a payload staged\n * *after* the claimant's render-phase peek available for the window it\n * belongs to. */\nexport function claimPdfPreviewStage(stage: PendingPdfStage): void {\n if (pending === stage) pending = null;\n}\n"]}