react-os-shell 4.15.0 → 4.21.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 (95) hide show
  1. package/README.md +46 -0
  2. package/dist/{Browser-XV34CJFP.js → Browser-6ACRB3XK.js} +9 -6
  3. package/dist/Browser-6ACRB3XK.js.map +1 -0
  4. package/dist/{Calculator-FFCYNYU3.js → Calculator-QCAJ6ITI.js} +8 -5
  5. package/dist/Calculator-QCAJ6ITI.js.map +1 -0
  6. package/dist/ConfirmDialog-WAM5OKAV.js +3 -0
  7. package/dist/{ConfirmDialog-CXHJCDUL.js.map → ConfirmDialog-WAM5OKAV.js.map} +1 -1
  8. package/dist/{CurrencyConverter-3NCK7WY6.js → CurrencyConverter-PCYC6RL5.js} +8 -5
  9. package/dist/CurrencyConverter-PCYC6RL5.js.map +1 -0
  10. package/dist/{Documents-S6EQOLUF.js → Documents-XME7555L.js} +10 -7
  11. package/dist/Documents-XME7555L.js.map +1 -0
  12. package/dist/{Files-TFZNYXAH.js → Files-6Q4HZKZD.js} +16 -12
  13. package/dist/Files-6Q4HZKZD.js.map +1 -0
  14. package/dist/{Notepad-EAA6CXZX.js → Notepad-W6LXMKWD.js} +10 -7
  15. package/dist/Notepad-W6LXMKWD.js.map +1 -0
  16. package/dist/{PomodoroTimer-BXI752XM.js → PomodoroTimer-YABFEPEV.js} +10 -7
  17. package/dist/PomodoroTimer-YABFEPEV.js.map +1 -0
  18. package/dist/{Preview-ZKYM7A5X.js → Preview-TYJQ6ASR.js} +11 -8
  19. package/dist/Preview-TYJQ6ASR.js.map +1 -0
  20. package/dist/{Sidebar-IMACKSHS.js → Sidebar-B3LY5T2U.js} +5 -4
  21. package/dist/Sidebar-B3LY5T2U.js.map +1 -0
  22. package/dist/{Spreadsheet-BXOPROMB.js → Spreadsheet-TAO2PWRL.js} +10 -7
  23. package/dist/Spreadsheet-TAO2PWRL.js.map +1 -0
  24. package/dist/{Stock-BEQCSYNT.js → Stock-IPLLRUOC.js} +8 -5
  25. package/dist/Stock-IPLLRUOC.js.map +1 -0
  26. package/dist/{Weather-QPV6G7UN.js → Weather-SI5QN7X2.js} +8 -5
  27. package/dist/Weather-SI5QN7X2.js.map +1 -0
  28. package/dist/{WorldClock-I3ZE57V4.js → WorldClock-WYDSMUWX.js} +8 -5
  29. package/dist/WorldClock-WYDSMUWX.js.map +1 -0
  30. package/dist/apps/index.js +22 -19
  31. package/dist/apps/index.js.map +1 -1
  32. package/dist/chunk-22BNPLM4.js +46 -0
  33. package/dist/chunk-22BNPLM4.js.map +1 -0
  34. package/dist/{chunk-IVLDAUYM.js → chunk-3CUSBZWG.js} +3 -56
  35. package/dist/chunk-3CUSBZWG.js.map +1 -0
  36. package/dist/{chunk-VENYVK3L.js → chunk-4AKVHY4B.js} +48 -12
  37. package/dist/chunk-4AKVHY4B.js.map +1 -0
  38. package/dist/chunk-4OPGKWMT.js +7 -0
  39. package/dist/{chunk-F562QDVS.js.map → chunk-4OPGKWMT.js.map} +1 -1
  40. package/dist/chunk-C22EFSND.js +188 -0
  41. package/dist/chunk-C22EFSND.js.map +1 -0
  42. package/dist/{chunk-H4IEH6FK.js → chunk-EWDYCO65.js} +3 -3
  43. package/dist/{chunk-H4IEH6FK.js.map → chunk-EWDYCO65.js.map} +1 -1
  44. package/dist/{chunk-DINNWYVB.js → chunk-HYAW6KIL.js} +4 -45
  45. package/dist/chunk-HYAW6KIL.js.map +1 -0
  46. package/dist/{chunk-H3HFM4JQ.js → chunk-IGPSRRL7.js} +262 -712
  47. package/dist/chunk-IGPSRRL7.js.map +1 -0
  48. package/dist/chunk-L2O6B5KT.js +394 -0
  49. package/dist/chunk-L2O6B5KT.js.map +1 -0
  50. package/dist/{chunk-YS4ZW2P7.js → chunk-OI253GOA.js} +4 -4
  51. package/dist/{chunk-YS4ZW2P7.js.map → chunk-OI253GOA.js.map} +1 -1
  52. package/dist/{chunk-ZPCGIR62.js → chunk-OQ7GRU7G.js} +3 -3
  53. package/dist/{chunk-ZPCGIR62.js.map → chunk-OQ7GRU7G.js.map} +1 -1
  54. package/dist/{chunk-7J3HTJJ3.js → chunk-Q3AOIJ7B.js} +3 -3
  55. package/dist/{chunk-7J3HTJJ3.js.map → chunk-Q3AOIJ7B.js.map} +1 -1
  56. package/dist/{chunk-N2UW3CAS.js → chunk-QJS6RBH4.js} +3 -3
  57. package/dist/{chunk-N2UW3CAS.js.map → chunk-QJS6RBH4.js.map} +1 -1
  58. package/dist/chunk-UPGCISED.js +5015 -0
  59. package/dist/chunk-UPGCISED.js.map +1 -0
  60. package/dist/{chunk-LFTHOYKW.js → chunk-VX634UB2.js} +5 -5
  61. package/dist/{chunk-LFTHOYKW.js.map → chunk-VX634UB2.js.map} +1 -1
  62. package/dist/chunk-WNLPTLT6.js +56 -0
  63. package/dist/chunk-WNLPTLT6.js.map +1 -0
  64. package/dist/{chunk-QU5L5ZYM.js → chunk-X5PZYTAK.js} +3 -3
  65. package/dist/{chunk-QU5L5ZYM.js.map → chunk-X5PZYTAK.js.map} +1 -1
  66. package/dist/index-Czn1re5M.d.ts +1939 -0
  67. package/dist/index.d.ts +7 -1375
  68. package/dist/index.js +159 -4082
  69. package/dist/index.js.map +1 -1
  70. package/dist/shell.css +105 -0
  71. package/dist/styles.css +12 -501
  72. package/dist/ui/index.d.ts +3 -0
  73. package/dist/ui/index.js +11 -0
  74. package/dist/ui/index.js.map +1 -0
  75. package/dist/ui.css +425 -0
  76. package/package.json +7 -2
  77. package/dist/Browser-XV34CJFP.js.map +0 -1
  78. package/dist/Calculator-FFCYNYU3.js.map +0 -1
  79. package/dist/ConfirmDialog-CXHJCDUL.js +0 -4
  80. package/dist/CurrencyConverter-3NCK7WY6.js.map +0 -1
  81. package/dist/Documents-S6EQOLUF.js.map +0 -1
  82. package/dist/Files-TFZNYXAH.js.map +0 -1
  83. package/dist/Notepad-EAA6CXZX.js.map +0 -1
  84. package/dist/PomodoroTimer-BXI752XM.js.map +0 -1
  85. package/dist/Preview-ZKYM7A5X.js.map +0 -1
  86. package/dist/Sidebar-IMACKSHS.js.map +0 -1
  87. package/dist/Spreadsheet-BXOPROMB.js.map +0 -1
  88. package/dist/Stock-BEQCSYNT.js.map +0 -1
  89. package/dist/Weather-QPV6G7UN.js.map +0 -1
  90. package/dist/WorldClock-I3ZE57V4.js.map +0 -1
  91. package/dist/chunk-DINNWYVB.js.map +0 -1
  92. package/dist/chunk-F562QDVS.js +0 -7
  93. package/dist/chunk-H3HFM4JQ.js.map +0 -1
  94. package/dist/chunk-IVLDAUYM.js.map +0 -1
  95. package/dist/chunk-VENYVK3L.js.map +0 -1
@@ -0,0 +1,11 @@
1
+ export { ALT, ALT_SHIFT_D, ALT_SHIFT_E, ALT_SHIFT_N, Accordion, AuthScreen, Avatar, AvatarGroup, Banner, BarChart, Button_default as Button, CMD_A, CMD_DOT, CMD_ENTER, CMD_K, CMD_S, Card, ChatTemplate, Checkbox_default as Checkbox, CheckoutTemplate, ColoredBadge, ContainerFillChart, CountBadge, DashboardTemplate, DataTable, DataTablePage, DateRangePicker, DescriptionList, Divider, DonutChart, Drawer, ENTER, EmailTemplate, EmptyState, ErrorPage, FilePicker, FormField, FormLayoutPage, GalleryTemplate, Grid, HelpCenter, INPUT_BASE, INPUT_SIZES, Inline, Input_default as Input, InputNumber_default as InputNumber, Kanban, Label, ListFooter, ListLoadError, LoadingSpinner, MAX_FRACTION_DIGITS, MOD, Markdown, MediaUploadField, MediaUploadGrid, MetricBar, MilestoneTimeline, NativeSelect, NumericKeypad, PageHeader, Pagination, Paragraph, Radio_default as Radio, Result, SHIFT, SearchableSelect, Segmented, Select_default as Select, SidebarActionButton, SidebarGroupLabel, SidebarNavItem, Skeleton, Sparkline, Stack, StatCard, Statistic, StatusBadge, StatusBadgeProvider, Switch, Tabs, Text, Textarea_default as Textarea, TileButton_default as TileButton, Title, Tooltip, TopNav, appendKey, applyThemePrefs, backspace, formatDate, inputClasses, isMac, isSeverityTone, mediaFileName, resolveTheme, severityOf, toISODate, useClickOutside, useTheme } from '../chunk-UPGCISED.js';
2
+ export { Breadcrumbs } from '../chunk-HYAW6KIL.js';
3
+ export { EditableGrid } from '../chunk-DC6BYGZ6.js';
4
+ export { SidebarLayout } from '../chunk-YG45DAUT.js';
5
+ export { toast_default as toast } from '../chunk-4AKVHY4B.js';
6
+ export { VERSION } from '../chunk-4OPGKWMT.js';
7
+ export { PopupMenu, PopupMenuDivider, PopupMenuItem, PopupMenuLabel, ShellPrefsProvider, useIsMobile, useLocalStoragePrefs, useShellPrefs } from '../chunk-C22EFSND.js';
8
+ export { ConfirmProvider, Dialog, confirm, confirmDestructive, prompt, useConfirm, useFocusTrap, useScrollLock } from '../chunk-L2O6B5KT.js';
9
+ export { GLASS_DIVIDER, glassStyle } from '../chunk-3CUSBZWG.js';
10
+ //# sourceMappingURL=index.js.map
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}
package/dist/ui.css ADDED
@@ -0,0 +1,425 @@
1
+ /**
2
+ * react-os-shell/ui.css — styles for the UI kit, without the window chrome.
3
+ *
4
+ * Import this when you take `react-os-shell/ui`:
5
+ *
6
+ * import "react-os-shell/ui.css";
7
+ *
8
+ * Import EXACTLY ONE of this and `react-os-shell/styles.css` — the latter is
9
+ * the umbrella over this file plus shell.css, so taking both doubles every rule.
10
+ *
11
+ * You supply Tailwind v4 yourself (`@import "tailwindcss"` in your own entry,
12
+ * which every consumer already does). This file deliberately does NOT import
13
+ * it: a consumer mid-migration off another component library may need the
14
+ * utility and theme layers WITHOUT preflight, whose bare element selectors
15
+ * would otherwise out-specify that library's `:where()`-wrapped reset.
16
+ *
17
+ * Scope — what dark mode actually is here:
18
+ * - the accent theme variants, via themes.css below
19
+ * - [data-theme="dark"] overrides of Tailwind UTILITY CLASS NAMES. There are
20
+ * no `dark:` variants in this package; you write the light class and the
21
+ * remap happens to you. Escaped-important classes (.\!bg-blue-50) and alpha
22
+ * variants (.bg-white\/70) compile to different class names, so each needs
23
+ * its own rule — that is why the list is long rather than clever.
24
+ * - the reduce-transparency escape hatch, form-control dark styling, the
25
+ * coarse-pointer 16px rule that defeats iOS focus-zoom, StatusBadge tones,
26
+ * and scrollbars.
27
+ */
28
+
29
+ @import "./themes.css";
30
+
31
+ /* Tailwind v4 doesn't scan node_modules by default — tell it to scan this
32
+ package's compiled JS so utility classes used in kit components get
33
+ generated when consumers import react-os-shell/ui.css. */
34
+ @source "./**/*.js";
35
+
36
+ /* The default `utils/glass.ts` already assumes at `getMenuOpacity()`'s
37
+ fallback. Declared here so a ui-only consumer gets identical glass to a shell
38
+ consumer at rest, and has one knob to dial it. <Layout> overrides it inline
39
+ for shell consumers; reduce-transparency still beats both. */
40
+ :root {
41
+ --menu-opacity: 0.95;
42
+ }
43
+
44
+ /* ── Reduce transparency (Preferences → Customization) ──────────────────────
45
+ * When the user turns on "Reduce transparency", Layout adds this class to
46
+ * <html>. Two effects, applied globally so every present-and-future glass
47
+ * surface is covered without per-component edits:
48
+ * 1. Force every window/taskbar/menu opacity variable fully opaque, so the
49
+ * surfaces that tint via `rgb(... / var(--*-opacity))` become solid. The
50
+ * `!important` beats Layout's inline (non-important) custom-property writes.
51
+ * 2. Strip backdrop-filter everywhere — the blur is the GPU-expensive part on
52
+ * older machines. `!important` overrides both Tailwind `backdrop-blur-*`
53
+ * utilities and inline `backdropFilter` from glassStyle(). glassStyle()
54
+ * also returns a solid background under this class so menus/popups don't
55
+ * read as washed-out translucent panels once the blur is gone. */
56
+ html.rosh-reduce-transparency {
57
+ --active-header-opacity: 1 !important;
58
+ --active-content-opacity: 1 !important;
59
+ --inactive-header-opacity: 1 !important;
60
+ --inactive-content-opacity: 1 !important;
61
+ --menu-opacity: 1 !important;
62
+ --taskbar-opacity: 1 !important;
63
+ }
64
+ html.rosh-reduce-transparency *,
65
+ html.rosh-reduce-transparency *::before,
66
+ html.rosh-reduce-transparency *::after {
67
+ backdrop-filter: none !important;
68
+ -webkit-backdrop-filter: none !important;
69
+ }
70
+
71
+ /* ═══════════════════════════════════════════════════
72
+ Theme: Dark
73
+ ═══════════════════════════════════════════════════ */
74
+ [data-theme="dark"] {
75
+ color-scheme: dark;
76
+ }
77
+
78
+ /* Backgrounds */
79
+ [data-theme="dark"] .bg-white { background-color: #1e1e2e !important; }
80
+ [data-theme="dark"] .bg-gray-50 { background-color: #181825 !important; }
81
+ [data-theme="dark"] .bg-gray-100 { background-color: #1e1e2e !important; }
82
+ [data-theme="dark"] .bg-gray-200 { background-color: #313244 !important; }
83
+
84
+ /* Text */
85
+ [data-theme="dark"] .text-gray-900 { color: #cdd6f4 !important; }
86
+ [data-theme="dark"] .text-gray-800 { color: #bac2de !important; }
87
+ [data-theme="dark"] .text-gray-700 { color: #a6adc8 !important; }
88
+ [data-theme="dark"] .text-gray-600 { color: #9399b2 !important; }
89
+ [data-theme="dark"] .text-gray-500 { color: #7f849c !important; }
90
+ [data-theme="dark"] .text-gray-400 { color: #6c7086 !important; }
91
+
92
+ /* Borders */
93
+ [data-theme="dark"] .border-gray-200 { border-color: #313244 !important; }
94
+ [data-theme="dark"] .border-gray-300 { border-color: #45475a !important; }
95
+ [data-theme="dark"] .border-gray-100 { border-color: #2a2a3c !important; }
96
+
97
+ /* Cards, shadows, tables */
98
+ [data-theme="dark"] .shadow { --tw-shadow-color: rgba(0,0,0,0.4) !important; }
99
+ [data-theme="dark"] .shadow-lg { --tw-shadow-color: rgba(0,0,0,0.5) !important; }
100
+ [data-theme="dark"] .shadow-xl { --tw-shadow-color: rgba(0,0,0,0.5) !important; }
101
+ [data-theme="dark"] .shadow-2xl { --tw-shadow-color: rgba(0,0,0,0.6) !important; }
102
+ [data-theme="dark"] .divide-gray-200 > :not(:last-child) { border-color: #313244 !important; }
103
+ [data-theme="dark"] .divide-gray-100 > :not(:last-child) { border-color: #2a2a3c !important; }
104
+
105
+ /* Hover states */
106
+ [data-theme="dark"] .hover\:bg-gray-50:hover { background-color: #2a2a3c !important; }
107
+ [data-theme="dark"] .hover\:bg-gray-100:hover { background-color: #313244 !important; }
108
+ [data-theme="dark"] .hover\:bg-gray-200:hover { background-color: #45475a !important; }
109
+
110
+ /* Blue hover states — used by menu items, profile row, etc.
111
+ In dark mode swap the near-white bg for a dark blue tint and the dark
112
+ blue text for a light shade so highlighted items stay readable. */
113
+ [data-theme="dark"] .bg-blue-50,
114
+ [data-theme="dark"] .hover\:bg-blue-50:hover { background-color: rgba(59, 130, 246, 0.26) !important; }
115
+ [data-theme="dark"] .text-blue-700,
116
+ [data-theme="dark"] .hover\:text-blue-700:hover { color: #bfdbfe !important; }
117
+ [data-theme="dark"] .text-blue-600 { color: #93c5fd !important; }
118
+ [data-theme="dark"] .text-blue-800 { color: #bfdbfe !important; }
119
+
120
+ /* Bare *-100 highlight backgrounds + *-700 text (status pills, badges, avatars,
121
+ dialog icons) have no dark override otherwise, so they keep their bright
122
+ light-mode value and wash out the text on top. Mute the background to a low
123
+ tint and lighten the text so highlighted content stays readable in dark mode. */
124
+ [data-theme="dark"] .bg-blue-100 { background-color: rgba(59, 130, 246, 0.22) !important; }
125
+
126
+ /* Tailwind's `!` important modifier compiles to distinct class names
127
+ (`.\!bg-blue-100` etc.), so the EditableGrid selection utilities escape the
128
+ remaps above: the selection keeps its light-mode background while the cell
129
+ text goes light, making selected cells unreadable in dark mode. The
130
+ attribute selector outranks the utility's own !important. */
131
+ [data-theme="dark"] .\!bg-blue-50 { background-color: rgba(59, 130, 246, 0.26) !important; }
132
+ [data-theme="dark"] .\!bg-blue-100 { background-color: rgba(59, 130, 246, 0.22) !important; }
133
+ [data-theme="dark"] .\!bg-blue-200 { background-color: rgba(59, 130, 246, 0.35) !important; }
134
+ [data-theme="dark"] .\!text-gray-700 { color: #a6adc8 !important; }
135
+ [data-theme="dark"] .bg-green-100 { background-color: rgba(34, 197, 94, 0.18) !important; }
136
+ [data-theme="dark"] .bg-emerald-100 { background-color: rgba(16, 185, 129, 0.18) !important; }
137
+ [data-theme="dark"] .bg-sky-100 { background-color: rgba(14, 165, 233, 0.18) !important; }
138
+ [data-theme="dark"] .bg-amber-100 { background-color: rgba(245, 158, 11, 0.18) !important; }
139
+ [data-theme="dark"] .bg-yellow-100 { background-color: rgba(234, 179, 8, 0.18) !important; }
140
+ [data-theme="dark"] .bg-orange-100 { background-color: rgba(249, 115, 22, 0.18) !important; }
141
+ [data-theme="dark"] .bg-red-100 { background-color: rgba(239, 68, 68, 0.18) !important; }
142
+ [data-theme="dark"] .bg-indigo-100 { background-color: rgba(99, 102, 241, 0.18) !important; }
143
+ [data-theme="dark"] .bg-purple-100 { background-color: rgba(168, 85, 247, 0.18) !important; }
144
+ [data-theme="dark"] .text-green-700 { color: #86efac !important; }
145
+ [data-theme="dark"] .text-emerald-700 { color: #6ee7b7 !important; }
146
+ [data-theme="dark"] .text-sky-700 { color: #7dd3fc !important; }
147
+ [data-theme="dark"] .text-amber-700 { color: #fcd34d !important; }
148
+ [data-theme="dark"] .text-yellow-700 { color: #fde047 !important; }
149
+ [data-theme="dark"] .text-orange-700 { color: #fdba74 !important; }
150
+ [data-theme="dark"] .text-red-700 { color: #fca5a5 !important; }
151
+ [data-theme="dark"] .text-indigo-700 { color: #a5b4fc !important; }
152
+ [data-theme="dark"] .text-purple-700 { color: #d8b4fe !important; }
153
+
154
+ /* Brighten taskbar button text so it stays readable on dark glass.
155
+ The default override (#9399b2) is too dim for short button labels. */
156
+ [data-theme="dark"] .text-gray-600 { color: #a6adc8 !important; }
157
+
158
+ /* Translucent button backgrounds — Tailwind generates separate classes for the
159
+ /40 /60 alpha variants (bg-gray-50\/40 etc.) so the bare .bg-gray-50 override
160
+ doesn't match. Override them explicitly with dark equivalents so taskbar
161
+ tabs and the start-menu button don't look too bright in Dark mode. */
162
+ [data-theme="dark"] .bg-gray-50\/40 { background-color: rgba(24, 24, 37, 0.4) !important; }
163
+ /* /50 and /60 are used as in-window PANEL surfaces (sidebars, form sections,
164
+ popup cards) — keep them solid in dark so a bright wallpaper can't bleed
165
+ through the window glass and wash them out. */
166
+ [data-theme="dark"] .bg-gray-50\/50 { background-color: #181825 !important; }
167
+ [data-theme="dark"] .bg-gray-50\/60 { background-color: #181825 !important; }
168
+ [data-theme="dark"] .hover\:bg-gray-50\/50:hover { background-color: #181825 !important; }
169
+ [data-theme="dark"] .bg-gray-100\/40 { background-color: rgba(30, 30, 46, 0.4) !important; }
170
+ [data-theme="dark"] .bg-gray-100\/60 { background-color: rgba(30, 30, 46, 0.6) !important; }
171
+ [data-theme="dark"] .bg-gray-200\/40 { background-color: rgba(49, 50, 68, 0.4) !important; }
172
+ [data-theme="dark"] .bg-gray-200\/50 { background-color: rgba(49, 50, 68, 0.5) !important; }
173
+ [data-theme="dark"] .bg-blue-100\/60 { background-color: rgba(59, 130, 246, 0.22) !important; }
174
+ /* Pale-blue tinted panels (selected rows, inline-reply box) — scale the
175
+ .bg-blue-50 dark tint (0.26) down through the alpha steps. */
176
+ [data-theme="dark"] .bg-blue-50\/30 { background-color: rgba(59, 130, 246, 0.10) !important; }
177
+ [data-theme="dark"] .bg-blue-50\/40 { background-color: rgba(59, 130, 246, 0.12) !important; }
178
+ [data-theme="dark"] .bg-blue-50\/50 { background-color: rgba(59, 130, 246, 0.15) !important; }
179
+ [data-theme="dark"] .bg-blue-50\/60 { background-color: rgba(59, 130, 246, 0.18) !important; }
180
+ [data-theme="dark"] .border-gray-200\/40 { border-color: rgba(69, 71, 90, 0.4) !important; }
181
+ [data-theme="dark"] .border-gray-300\/40 { border-color: rgba(88, 91, 112, 0.4) !important; }
182
+ [data-theme="dark"] .border-blue-400\/60 { border-color: rgba(96, 165, 250, 0.45) !important; }
183
+ [data-theme="dark"] .hover\:bg-gray-200\/40:hover { background-color: rgba(49, 50, 68, 0.4) !important; }
184
+
185
+ /* White alpha variants — the bare .bg-white override doesn't match the /50../95
186
+ alpha classes, so cards (the Widget gallery), the Exposé snapshot title pill
187
+ and desktop overlays stayed bright white in dark mode while their (remapped,
188
+ light) text washed out. Map them to the dark window tint. */
189
+ [data-theme="dark"] .bg-white\/50 { background-color: rgba(30, 30, 46, 0.5) !important; }
190
+ [data-theme="dark"] .bg-white\/60 { background-color: rgba(30, 30, 46, 0.6) !important; }
191
+ [data-theme="dark"] .bg-white\/70 { background-color: rgba(30, 30, 46, 0.7) !important; }
192
+ [data-theme="dark"] .bg-white\/80 { background-color: rgba(30, 30, 46, 0.85) !important; }
193
+ [data-theme="dark"] .bg-white\/90 { background-color: rgba(30, 30, 46, 0.92) !important; }
194
+ [data-theme="dark"] .bg-white\/95 { background-color: rgba(30, 30, 46, 0.95) !important; }
195
+ [data-theme="dark"] .bg-blue-50\/70 { background-color: rgba(59, 130, 246, 0.22) !important; }
196
+
197
+ /* Inputs — apply the form-input dark styling, but skip elements that
198
+ explicitly opt into a transparent background (e.g. the start-menu
199
+ search) so they keep their parent's frosted-glass tile. */
200
+ [data-theme="dark"] input:not(.bg-transparent):not([type="checkbox"]):not([type="radio"]):not([type="color"]):not([type="file"]),
201
+ [data-theme="dark"] select:not(.bg-transparent),
202
+ [data-theme="dark"] textarea:not(.bg-transparent) {
203
+ background-color: #181825 !important;
204
+ border-color: #45475a !important;
205
+ color: #cdd6f4 !important;
206
+ }
207
+
208
+ /* iOS auto-zooms the viewport when a focused form control's computed
209
+ font-size is below 16px. On phones/tablets (same breakpoint as
210
+ useIsMobile) pin text-bearing controls to 16px so focusing them never
211
+ triggers the zoom. This keeps pinch-zoom working, unlike a
212
+ `maximum-scale=1` viewport lock. !important beats Tailwind text-*
213
+ utilities; the :not() list mirrors the dark-input rule above and skips
214
+ controls that don't take a text caret. */
215
+ @media (max-width: 767px), (pointer: coarse) {
216
+ input:not([type="checkbox"]):not([type="radio"]):not([type="color"]):not([type="file"]),
217
+ select,
218
+ textarea {
219
+ font-size: 16px !important;
220
+ }
221
+ }
222
+
223
+ /* Status badges — tone down brightness in dark mode (matches StatusBadge.tsx semantic groups) */
224
+ [data-theme="dark"] .bg-gray-100.text-gray-800 { background-color: #313244 !important; color: #a6adc8 !important; }
225
+ [data-theme="dark"] .bg-gray-300.text-gray-800 { background-color: #45475a !important; color: #cdd6f4 !important; }
226
+ [data-theme="dark"] .bg-green-100.text-green-800 { background-color: rgba(34, 197, 94, 0.18) !important; color: #86efac !important; }
227
+ [data-theme="dark"] .bg-blue-100.text-blue-800 { background-color: rgba(59, 130, 246, 0.18) !important; color: #93c5fd !important; }
228
+ [data-theme="dark"] .bg-indigo-100.text-indigo-800 { background-color: rgba(99, 102, 241, 0.18) !important; color: #a5b4fc !important; }
229
+ [data-theme="dark"] .bg-sky-100.text-sky-800 { background-color: rgba(14, 165, 233, 0.18) !important; color: #7dd3fc !important; }
230
+ [data-theme="dark"] .bg-yellow-100.text-yellow-800 { background-color: rgba(234, 179, 8, 0.18) !important; color: #fde68a !important; }
231
+ [data-theme="dark"] .bg-orange-100.text-orange-800 { background-color: rgba(249, 115, 22, 0.18) !important; color: #fdba74 !important; }
232
+ [data-theme="dark"] .bg-red-100.text-red-800 { background-color: rgba(239, 68, 68, 0.18) !important; color: #fca5a5 !important; }
233
+
234
+ /* ── Extended dark-mode tint families ────────────────────────────────
235
+ Upstreamed 2026-06-13 from the admin portal's 2026-06-10 audit so all
236
+ consumers share one source of truth. Every hue used by chips/badges
237
+ (tag colors, group colors, status accents, deal stages), notice
238
+ banners, verdict panels, row tints and danger buttons gets the same
239
+ scale as the families above: -50 → 10% tint, -100 → 18%, -200 → ~30%,
240
+ borders -200 → 32% / -300 → 45%, and dark inks flip to the
241
+ -400/-300/-200 light end of their scale.
242
+ Deliberately NOT remapped: game surfaces that read as intentionally
243
+ light (Calculator LCD bg-slate-200/text-slate-600/800, 2048/Chess
244
+ tiles, weather gradients), the Customization theme-preview swatches
245
+ (bg-pink-50/90, bg-green-50/90, bg-blue-50/90, bg-gray-200/90,
246
+ bg-pink-200, bg-green-200 — they must show each theme's real colors),
247
+ and low white/black alphas used as glass highlights on wallpaper. */
248
+
249
+ /* Red */
250
+ [data-theme="dark"] .bg-red-50 { background-color: rgba(239, 68, 68, 0.10) !important; }
251
+ [data-theme="dark"] .hover\:bg-red-50:hover { background-color: rgba(239, 68, 68, 0.16) !important; }
252
+ [data-theme="dark"] .active\:bg-red-50:active { background-color: rgba(239, 68, 68, 0.16) !important; }
253
+ [data-theme="dark"] .bg-red-50\/30 { background-color: rgba(239, 68, 68, 0.06) !important; }
254
+ [data-theme="dark"] .bg-red-50\/50 { background-color: rgba(239, 68, 68, 0.08) !important; }
255
+ [data-theme="dark"] .hover\:bg-red-100:hover { background-color: rgba(239, 68, 68, 0.22) !important; }
256
+ [data-theme="dark"] .border-red-200 { border-color: rgba(239, 68, 68, 0.35) !important; }
257
+ [data-theme="dark"] .border-red-300,
258
+ [data-theme="dark"] .hover\:border-red-300:hover { border-color: rgba(239, 68, 68, 0.45) !important; }
259
+ [data-theme="dark"] .text-red-600,
260
+ [data-theme="dark"] .hover\:text-red-600:hover { color: #f87171 !important; }
261
+ [data-theme="dark"] .hover\:text-red-700:hover { color: #fca5a5 !important; }
262
+ [data-theme="dark"] .text-red-800,
263
+ [data-theme="dark"] .hover\:text-red-800:hover { color: #fecaca !important; }
264
+ [data-theme="dark"] .text-red-900 { color: #fecaca !important; }
265
+
266
+ /* Amber */
267
+ [data-theme="dark"] .bg-amber-50 { background-color: rgba(245, 158, 11, 0.10) !important; }
268
+ [data-theme="dark"] .hover\:bg-amber-50:hover { background-color: rgba(245, 158, 11, 0.14) !important; }
269
+ [data-theme="dark"] .bg-amber-50\/30 { background-color: rgba(245, 158, 11, 0.06) !important; }
270
+ [data-theme="dark"] .bg-amber-50\/40 { background-color: rgba(245, 158, 11, 0.07) !important; }
271
+ [data-theme="dark"] .bg-amber-50\/50 { background-color: rgba(245, 158, 11, 0.08) !important; }
272
+ [data-theme="dark"] .hover\:bg-amber-100:hover { background-color: rgba(245, 158, 11, 0.24) !important; }
273
+ [data-theme="dark"] .bg-amber-200 { background-color: rgba(245, 158, 11, 0.28) !important; }
274
+ [data-theme="dark"] .border-amber-200 { border-color: rgba(245, 158, 11, 0.35) !important; }
275
+ [data-theme="dark"] .border-amber-300,
276
+ [data-theme="dark"] .hover\:border-amber-300:hover { border-color: rgba(245, 158, 11, 0.45) !important; }
277
+ [data-theme="dark"] .text-amber-600,
278
+ [data-theme="dark"] .hover\:text-amber-600:hover { color: #fbbf24 !important; }
279
+ [data-theme="dark"] .text-amber-800 { color: #fde68a !important; }
280
+ [data-theme="dark"] .text-amber-900 { color: #fde68a !important; }
281
+
282
+ /* Yellow */
283
+ [data-theme="dark"] .bg-yellow-50 { background-color: rgba(234, 179, 8, 0.10) !important; }
284
+ [data-theme="dark"] .bg-yellow-50\/50 { background-color: rgba(234, 179, 8, 0.08) !important; }
285
+ [data-theme="dark"] .border-yellow-200 { border-color: rgba(234, 179, 8, 0.35) !important; }
286
+ [data-theme="dark"] .border-yellow-300 { border-color: rgba(234, 179, 8, 0.45) !important; }
287
+ [data-theme="dark"] .text-yellow-600,
288
+ [data-theme="dark"] .hover\:text-yellow-600:hover { color: #facc15 !important; }
289
+ [data-theme="dark"] .text-yellow-900 { color: #fef08a !important; }
290
+
291
+ /* Green / emerald */
292
+ [data-theme="dark"] .bg-green-50 { background-color: rgba(34, 197, 94, 0.10) !important; }
293
+ [data-theme="dark"] .hover\:bg-green-50:hover { background-color: rgba(34, 197, 94, 0.14) !important; }
294
+ [data-theme="dark"] .bg-green-50\/40 { background-color: rgba(34, 197, 94, 0.06) !important; }
295
+ [data-theme="dark"] .border-green-200 { border-color: rgba(34, 197, 94, 0.32) !important; }
296
+ [data-theme="dark"] .border-green-300,
297
+ [data-theme="dark"] .hover\:border-green-300:hover { border-color: rgba(34, 197, 94, 0.45) !important; }
298
+ [data-theme="dark"] .text-green-600,
299
+ [data-theme="dark"] .hover\:text-green-600:hover { color: #4ade80 !important; }
300
+ [data-theme="dark"] .text-green-900,
301
+ [data-theme="dark"] .hover\:text-green-800:hover { color: #bbf7d0 !important; }
302
+ [data-theme="dark"] .bg-emerald-50 { background-color: rgba(16, 185, 129, 0.10) !important; }
303
+ [data-theme="dark"] .hover\:bg-emerald-100:hover { background-color: rgba(16, 185, 129, 0.24) !important; }
304
+ [data-theme="dark"] .border-emerald-200 { border-color: rgba(16, 185, 129, 0.32) !important; }
305
+ [data-theme="dark"] .border-emerald-300 { border-color: rgba(16, 185, 129, 0.45) !important; }
306
+ [data-theme="dark"] .text-emerald-600 { color: #34d399 !important; }
307
+ [data-theme="dark"] .text-emerald-800 { color: #a7f3d0 !important; }
308
+
309
+ /* Orange */
310
+ [data-theme="dark"] .bg-orange-50 { background-color: rgba(249, 115, 22, 0.10) !important; }
311
+ [data-theme="dark"] .hover\:bg-orange-50:hover { background-color: rgba(249, 115, 22, 0.14) !important; }
312
+ [data-theme="dark"] .hover\:bg-orange-200:hover { background-color: rgba(249, 115, 22, 0.30) !important; }
313
+ [data-theme="dark"] .border-orange-200 { border-color: rgba(249, 115, 22, 0.32) !important; }
314
+ [data-theme="dark"] .border-orange-300,
315
+ [data-theme="dark"] .hover\:border-orange-300:hover { border-color: rgba(249, 115, 22, 0.45) !important; }
316
+ [data-theme="dark"] .text-orange-600 { color: #fb923c !important; }
317
+ [data-theme="dark"] .text-orange-900 { color: #fed7aa !important; }
318
+
319
+ /* Sky / teal / cyan */
320
+ [data-theme="dark"] .bg-sky-50 { background-color: rgba(14, 165, 233, 0.10) !important; }
321
+ [data-theme="dark"] .border-sky-300 { border-color: rgba(14, 165, 233, 0.45) !important; }
322
+ [data-theme="dark"] .bg-teal-50 { background-color: rgba(20, 184, 166, 0.10) !important; }
323
+ [data-theme="dark"] .bg-teal-100 { background-color: rgba(20, 184, 166, 0.18) !important; }
324
+ [data-theme="dark"] .text-teal-600 { color: #2dd4bf !important; }
325
+ [data-theme="dark"] .text-teal-700 { color: #5eead4 !important; }
326
+ [data-theme="dark"] .text-teal-800 { color: #99f6e4 !important; }
327
+ [data-theme="dark"] .text-cyan-600 { color: #22d3ee !important; }
328
+
329
+ /* Purple / violet */
330
+ [data-theme="dark"] .bg-purple-50 { background-color: rgba(168, 85, 247, 0.10) !important; }
331
+ [data-theme="dark"] .hover\:bg-purple-50:hover { background-color: rgba(168, 85, 247, 0.14) !important; }
332
+ [data-theme="dark"] .border-purple-200 { border-color: rgba(168, 85, 247, 0.32) !important; }
333
+ [data-theme="dark"] .border-purple-300,
334
+ [data-theme="dark"] .hover\:border-purple-300:hover { border-color: rgba(168, 85, 247, 0.45) !important; }
335
+ [data-theme="dark"] .text-purple-600 { color: #c084fc !important; }
336
+ [data-theme="dark"] .text-purple-800,
337
+ [data-theme="dark"] .text-purple-900 { color: #e9d5ff !important; }
338
+ [data-theme="dark"] .bg-violet-100 { background-color: rgba(139, 92, 246, 0.18) !important; }
339
+ [data-theme="dark"] .border-violet-300 { border-color: rgba(139, 92, 246, 0.45) !important; }
340
+ [data-theme="dark"] .text-violet-800 { color: #ddd6fe !important; }
341
+
342
+ /* Pink / rose */
343
+ [data-theme="dark"] .bg-pink-100 { background-color: rgba(236, 72, 153, 0.18) !important; }
344
+ [data-theme="dark"] .border-pink-200 { border-color: rgba(236, 72, 153, 0.32) !important; }
345
+ [data-theme="dark"] .border-pink-300 { border-color: rgba(236, 72, 153, 0.45) !important; }
346
+ [data-theme="dark"] .text-pink-600 { color: #f472b6 !important; }
347
+ [data-theme="dark"] .text-pink-800,
348
+ [data-theme="dark"] .text-pink-900 { color: #fbcfe8 !important; }
349
+ [data-theme="dark"] .bg-rose-100 { background-color: rgba(244, 63, 94, 0.18) !important; }
350
+ [data-theme="dark"] .text-rose-700 { color: #fda4af !important; }
351
+ [data-theme="dark"] .text-rose-800 { color: #fecdd3 !important; }
352
+
353
+ /* Indigo / slate */
354
+ [data-theme="dark"] .bg-indigo-50 { background-color: rgba(99, 102, 241, 0.15) !important; }
355
+ [data-theme="dark"] .hover\:bg-indigo-100:hover { background-color: rgba(99, 102, 241, 0.25) !important; }
356
+ [data-theme="dark"] .text-indigo-600 { color: #a5b4fc !important; }
357
+ [data-theme="dark"] .border-indigo-300 { border-color: rgba(99, 102, 241, 0.45) !important; }
358
+ [data-theme="dark"] .text-slate-700 { color: #cbd5e1 !important; } /* slate-600/800 left for the Calculator LCD */
359
+ [data-theme="dark"] .border-slate-300 { border-color: rgba(148, 163, 184, 0.35) !important; }
360
+
361
+ /* Blue — gaps beyond the accent remaps above. Hover/active steps sit one
362
+ ladder step above their resting tint (base .26 → active .30; the /30../50
363
+ alpha ladder steps 0.10/0.12/0.15 → hovers 0.12/0.15/0.18). */
364
+ [data-theme="dark"] .bg-blue-200 { background-color: rgba(59, 130, 246, 0.35) !important; }
365
+ [data-theme="dark"] .active\:bg-blue-200:active { background-color: rgba(59, 130, 246, 0.40) !important; }
366
+ [data-theme="dark"] .bg-blue-200\/60 { background-color: rgba(59, 130, 246, 0.20) !important; }
367
+ [data-theme="dark"] .hover\:bg-blue-100:hover { background-color: rgba(59, 130, 246, 0.26) !important; }
368
+ [data-theme="dark"] .active\:bg-blue-50:active { background-color: rgba(59, 130, 246, 0.30) !important; }
369
+ [data-theme="dark"] .hover\:bg-blue-50\/30:hover { background-color: rgba(59, 130, 246, 0.12) !important; }
370
+ [data-theme="dark"] .hover\:bg-blue-50\/40:hover { background-color: rgba(59, 130, 246, 0.15) !important; }
371
+ [data-theme="dark"] .hover\:bg-blue-50\/50:hover { background-color: rgba(59, 130, 246, 0.18) !important; }
372
+ [data-theme="dark"] .border-blue-100 { border-color: rgba(59, 130, 246, 0.22) !important; }
373
+ [data-theme="dark"] .border-blue-200,
374
+ [data-theme="dark"] .hover\:border-blue-200:hover { border-color: rgba(59, 130, 246, 0.32) !important; }
375
+ [data-theme="dark"] .border-blue-300,
376
+ [data-theme="dark"] .hover\:border-blue-300:hover { border-color: rgba(59, 130, 246, 0.45) !important; }
377
+ [data-theme="dark"] .border-blue-300\/50 { border-color: rgba(59, 130, 246, 0.25) !important; }
378
+ [data-theme="dark"] .text-blue-900 { color: #bfdbfe !important; }
379
+ [data-theme="dark"] .hover\:text-blue-600:hover { color: #93c5fd !important; }
380
+ [data-theme="dark"] .hover\:text-blue-800:hover { color: #bfdbfe !important; }
381
+ /* File-input buttons (file: compiles to ::file-selector-button) */
382
+ [data-theme="dark"] .file\:bg-blue-50::file-selector-button { background-color: rgba(59, 130, 246, 0.26) !important; }
383
+ [data-theme="dark"] .file\:text-blue-700::file-selector-button { color: #bfdbfe !important; }
384
+ [data-theme="dark"] .hover\:file\:bg-blue-100:hover::file-selector-button { background-color: rgba(59, 130, 246, 0.26) !important; }
385
+
386
+ /* Gray interaction-state variants (separate class names from the bare remaps) */
387
+ [data-theme="dark"] .hover\:text-gray-600:hover,
388
+ [data-theme="dark"] .hover\:text-gray-700:hover { color: #a6adc8 !important; }
389
+ [data-theme="dark"] .hover\:text-gray-800:hover { color: #bac2de !important; }
390
+ [data-theme="dark"] .hover\:text-gray-900:hover,
391
+ [data-theme="dark"] .active\:text-gray-900:active { color: #cdd6f4 !important; }
392
+ [data-theme="dark"] .active\:bg-gray-100:active { background-color: #313244 !important; }
393
+ [data-theme="dark"] .active\:bg-gray-200:active { background-color: #45475a !important; }
394
+ [data-theme="dark"] .active\:bg-gray-300:active,
395
+ [data-theme="dark"] .hover\:bg-gray-300:hover { background-color: #585b70 !important; }
396
+ [data-theme="dark"] .disabled\:bg-gray-100:disabled { background-color: #1e1e2e !important; }
397
+ [data-theme="dark"] .disabled\:bg-gray-200:disabled { background-color: #313244 !important; }
398
+ [data-theme="dark"] .hover\:bg-gray-200\/50:hover { background-color: rgba(69, 71, 90, 0.5) !important; }
399
+ [data-theme="dark"] .even\:bg-gray-50\/50:nth-child(even) { background-color: rgba(24, 24, 37, 0.5) !important; }
400
+ [data-theme="dark"] .border-gray-50,
401
+ [data-theme="dark"] .divide-gray-50 > :not(:last-child) { border-color: #2a2a3c !important; }
402
+ [data-theme="dark"] .border-gray-200\/50 { border-color: rgba(49, 50, 68, 0.6) !important; }
403
+ [data-theme="dark"] .hover\:border-gray-200:hover { border-color: #313244 !important; }
404
+ [data-theme="dark"] .hover\:border-gray-300:hover { border-color: #45475a !important; }
405
+
406
+ /* White panel surfaces / solid black ink. hover-to-white means "emphasize",
407
+ which on dark glass means a solid panel tint; hover-to-black ink flips to
408
+ brighter, not darker (e.g. the taskbar bell). */
409
+ [data-theme="dark"] .bg-white\/85 { background-color: rgba(30, 30, 46, 0.85) !important; }
410
+ [data-theme="dark"] .hover\:bg-white:hover { background-color: #1e1e2e !important; }
411
+ [data-theme="dark"] .text-black { color: #cdd6f4 !important; }
412
+ [data-theme="dark"] .hover\:text-black:hover { color: #ffffff !important; }
413
+
414
+ /* Scrollbar */
415
+ /* Force visible scrollbars on editable grids (macOS hides them by default) */
416
+ .grid-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
417
+ .grid-scroll::-webkit-scrollbar-track { background: #f1f5f9; }
418
+ .grid-scroll::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 5px; border: 2px solid #f1f5f9; }
419
+ .grid-scroll::-webkit-scrollbar-thumb:hover { background: #94a3b8; }
420
+ .grid-scroll::-webkit-scrollbar-corner { background: #f1f5f9; }
421
+
422
+ [data-theme="dark"] ::-webkit-scrollbar { width: 8px; height: 8px; }
423
+ [data-theme="dark"] ::-webkit-scrollbar-track { background: #181825; }
424
+ [data-theme="dark"] ::-webkit-scrollbar-thumb { background: #45475a; border-radius: 4px; }
425
+ [data-theme="dark"] ::-webkit-scrollbar-thumb:hover { background: #585b70; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-os-shell",
3
- "version": "4.15.0",
3
+ "version": "4.21.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",
@@ -33,7 +33,12 @@
33
33
  "import": "./dist/markup/index.js",
34
34
  "default": "./dist/markup/index.js"
35
35
  },
36
+ "./ui": {
37
+ "types": "./dist/ui/index.d.ts",
38
+ "import": "./dist/ui/index.js"
39
+ },
36
40
  "./styles.css": "./dist/styles.css",
41
+ "./ui.css": "./dist/ui.css",
37
42
  "./themes.css": "./dist/themes.css"
38
43
  },
39
44
  "files": [
@@ -81,7 +86,7 @@
81
86
  "xlsx": "^0.18.5"
82
87
  },
83
88
  "scripts": {
84
- "build": "tsup && cp src/styles.css src/themes.css dist/",
89
+ "build": "tsup && cp src/*.css dist/",
85
90
  "dev": "tsup --watch",
86
91
  "typecheck": "tsc --noEmit && tsc -p tsconfig.test.json",
87
92
  "test": "node scripts/test.mjs",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/apps/Browser.tsx"],"names":[],"mappings":"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","file":"Browser-XV34CJFP.js","sourcesContent":["/**\n * Browser — minimal iframe-backed web browser app.\n *\n * URL bar with back / forward / refresh, navigable bookmark bar\n * (persisted to localStorage), and a graceful \"open in new tab\"\n * escape hatch since most major sites refuse iframe embedding via\n * X-Frame-Options or Content-Security-Policy.\n */\nimport { useEffect, useRef, useState } from 'react';\nimport { WindowTitle } from '../shell/Modal';\nimport { confirm } from '../shell/ConfirmDialog';\nimport AboutApp from './_about';\nimport { peekBrowserStartUrl, claimBrowserStartUrl, type PendingStartUrl } from './_browserStage';\n\ninterface Bookmark {\n label: string;\n url: string;\n}\n\nconst BOOKMARKS_KEY = 'react-os-shell:browser-bookmarks';\nconst HOMEPAGE_KEY = 'react-os-shell:browser-homepage';\nconst DEFAULT_HOMEPAGE = 'https://en.wikipedia.org/wiki/Main_Page';\n// Search provider (DuckDuckGo, no tracking). Hoisted to a constant so the\n// provider is stated in exactly one place — note it is deliberately NOT\n// framable, hence its presence in BLOCKED_HOSTS below.\nconst SEARCH_URL = 'https://duckduckgo.com/?q=';\nconst DEFAULT_BOOKMARKS: Bookmark[] = [\n { label: 'Wikipedia', url: 'https://en.wikipedia.org/wiki/Main_Page' },\n // DevDocs rather than MDN: MDN sends `x-frame-options: DENY`, so shipping it\n // as a default bookmark shipped a guaranteed-blank pane (BG#00374). DevDocs\n // carries the same API reference content and permits embedding.\n { label: 'DevDocs', url: 'https://devdocs.io' },\n { label: 'Example', url: 'https://example.com' },\n];\n\n// A dotted single token is ambiguous: `example.com` is a host, but `node.js`,\n// `web.config` and `array.map` are search queries the user expects to search.\n// The only offline signal is the last label, so we treat a bare token as a host\n// ONLY when it ends in a real TLD; everything else searches. A host on an\n// exotic TLD missing from this list is still reachable by typing the scheme\n// (`https://foo.zuerich`), which short-circuits before this check.\nconst KNOWN_TLDS = new Set([\n // ISO 3166-1 alpha-2 ccTLDs. Mind the lookalikes: `md`, `py`, `rs` and `sh`\n // ARE countries (`foo.sh` is a real host), while `js` and `ts` are not.\n 'ac ad ae af ag ai al am ao aq ar as at au aw ax az ba bb bd be bf bg bh bi',\n 'bj bm bn bo br bs bt bw by bz ca cc cd cf cg ch ci ck cl cm cn co cr cu cv',\n 'cw cx cy cz de dj dk dm do dz ec ee eg er es et eu fi fj fk fm fo fr ga gd',\n 'ge gf gg gh gi gl gm gn gp gq gr gs gt gu gw gy hk hm hn hr ht hu id ie il',\n 'im in io iq ir is it je jm jo jp ke kg kh ki km kn kp kr kw ky kz la lb lc',\n 'li lk lr ls lt lu lv ly ma mc md me mg mh mk ml mm mn mo mp mq mr ms mt mu',\n 'mv mw mx my mz na nc ne nf ng ni nl no np nr nu nz om pa pe pf pg ph pk pl',\n 'pm pn pr ps pt pw py qa re ro rs ru rw sa sb sc sd se sg sh si sk sl sm sn',\n 'so sr ss st su sv sx sy sz tc td tf tg th tj tk tl tm tn to tr tt tv tw tz',\n 'ua ug uk us uy uz va vc ve vg vi vn vu wf ws ye yt za zm zw',\n // Common gTLDs, plus the ones this business actually uses (`design`).\n 'com net org edu gov mil int info biz name pro app dev xyz online site tech',\n 'store shop cloud design digital agency studio media news blog live life',\n 'world today email systems solutions services software page link space host',\n 'press wiki team group works careers finance capital energy academy',\n].join(' ').split(' '));\n\n// `example.com`, `sub.example.co.uk`, `example.com/path` → host; `node.js` → search.\nfunction isProbableHost(s: string): boolean {\n const m = /^([\\w-]+\\.)+([a-z]{2,})(\\/.*)?$/i.exec(s);\n return m !== null && KNOWN_TLDS.has(m[2].toLowerCase());\n}\n\nfunction normalizeUrl(input: string): string {\n let s = input.trim();\n if (!s) return '';\n // Already a URL? Otherwise treat as a search query.\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(s)) return s;\n if (isProbableHost(s)) return 'https://' + s;\n return SEARCH_URL + encodeURIComponent(s);\n}\n\n// Sites known to refuse iframe embedding via X-Frame-Options or CSP. There's\n// no workaround inside an iframe — the browser blocks the load. We keep a\n// list so we can short-circuit to a friendly \"open in new tab\" panel\n// instead of letting the browser's blank \"refused to connect\" error\n// through. Subdomain match: `mail.google.com` matches `google.com`.\nconst BLOCKED_HOSTS = [\n // The two hosts this app itself routes to or ships as a default bookmark.\n // Their absence here was BG#00374: an address-bar search went to a blank\n // pane rather than the panel below, reading as \"search does nothing\".\n // Verified live 2026-07-16 — duckduckgo.com: `x-frame-options: SAMEORIGIN` +\n // `frame-ancestors 'self' https://html.duckduckgo.com` (the html/ and lite/\n // mirrors are no escape hatch: `frame-ancestors 'self'`);\n // developer.mozilla.org: `x-frame-options: DENY` (kept even though the\n // default bookmark moved to DevDocs — existing users have MDN persisted in\n // localStorage, and it stays a thing people type).\n 'duckduckgo.com', 'developer.mozilla.org',\n 'google.com', 'gmail.com', 'youtube.com',\n 'facebook.com', 'instagram.com', 'whatsapp.com',\n 'twitter.com', 'x.com',\n 'github.com', 'gitlab.com',\n 'linkedin.com', 'reddit.com', 'pinterest.com',\n 'amazon.com', 'amazon.ca', 'amazon.co.uk',\n 'apple.com', 'icloud.com',\n 'microsoft.com', 'outlook.com', 'live.com', 'office.com',\n 'netflix.com', 'spotify.com',\n 'paypal.com', 'stripe.com',\n 'chat.openai.com', 'chatgpt.com', 'claude.ai',\n];\n\nfunction hostIsBlocked(href: string): boolean {\n try {\n const host = new URL(href).hostname.toLowerCase();\n return BLOCKED_HOSTS.some(b => host === b || host.endsWith('.' + b));\n } catch { return false; }\n}\n\nfunction loadBookmarks(): Bookmark[] {\n if (typeof window === 'undefined') return DEFAULT_BOOKMARKS;\n try {\n const raw = localStorage.getItem(BOOKMARKS_KEY);\n if (!raw) return DEFAULT_BOOKMARKS;\n const parsed = JSON.parse(raw);\n if (Array.isArray(parsed) && parsed.every(b => typeof b?.url === 'string')) return parsed;\n } catch {}\n return DEFAULT_BOOKMARKS;\n}\n\n// The consumer-facing `setBrowserStartUrl` lives in ./_browserStage so that\n// hosts importing it don't pull this module into their startup bundle. This\n// component drains the stage via the @internal peek/claim pair.\n\nfunction loadHomepage(): string {\n if (typeof window === 'undefined') return DEFAULT_HOMEPAGE;\n return localStorage.getItem(HOMEPAGE_KEY) || DEFAULT_HOMEPAGE;\n}\n\nexport default function Browser() {\n // One-shot drain of a staged start URL. The render phase only PEEKS — under\n // React 18 concurrent rendering the first render pass of this lazy component\n // can be discarded and replayed, so a destructive read here would lose the\n // payload (see Spreadsheet/Preview). The stage is cleared in the mount\n // effect below.\n const consumedRef = useRef<PendingStartUrl | null | undefined>(undefined);\n if (consumedRef.current === undefined) {\n consumedRef.current = peekBrowserStartUrl();\n }\n useEffect(() => {\n if (consumedRef.current != null) {\n claimBrowserStartUrl(consumedRef.current);\n }\n }, []);\n const startUrl = consumedRef.current ? normalizeUrl(consumedRef.current.url) : '';\n\n const [homepage, setHomepage] = useState(loadHomepage);\n const [url, setUrl] = useState(startUrl || homepage);\n const [inputUrl, setInputUrl] = useState(url);\n const [history, setHistory] = useState<string[]>([url]);\n const [historyIdx, setHistoryIdx] = useState(0);\n const [iframeKey, setIframeKey] = useState(0);\n const [bookmarks, setBookmarks] = useState<Bookmark[]>(loadBookmarks);\n const [showHelp, setShowHelp] = useState(false);\n // Inline \"name this bookmark\" popover state. Populated when the user\n // clicks the star icon to add — replaces the native window.prompt().\n const [bookmarkDraft, setBookmarkDraft] = useState<string | null>(null);\n const bookmarkInputRef = useRef<HTMLInputElement>(null);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n\n // Persist bookmarks.\n useEffect(() => {\n try { localStorage.setItem(BOOKMARKS_KEY, JSON.stringify(bookmarks)); } catch {}\n }, [bookmarks]);\n\n const navigate = (next: string) => {\n const n = normalizeUrl(next);\n if (!n) return;\n setUrl(n);\n setInputUrl(n);\n // Truncate forward history when navigating from a back state.\n setHistory(h => {\n const trimmed = h.slice(0, historyIdx + 1);\n trimmed.push(n);\n return trimmed;\n });\n setHistoryIdx(i => i + 1);\n setShowHelp(false);\n };\n\n const back = () => {\n if (historyIdx > 0) {\n const next = historyIdx - 1;\n setHistoryIdx(next);\n setUrl(history[next]);\n setInputUrl(history[next]);\n setShowHelp(false);\n }\n };\n\n const forward = () => {\n if (historyIdx < history.length - 1) {\n const next = historyIdx + 1;\n setHistoryIdx(next);\n setUrl(history[next]);\n setInputUrl(history[next]);\n setShowHelp(false);\n }\n };\n\n const refresh = () => setIframeKey(k => k + 1);\n\n const goHome = () => navigate(homepage);\n\n const openExternal = () => {\n if (typeof window !== 'undefined') window.open(url, '_blank', 'noopener,noreferrer');\n };\n\n const openBookmarkPopover = () => {\n setBookmarkDraft(titleFromUrl(url));\n // Focus + select happens in a useEffect once the input is mounted.\n };\n\n const commitBookmark = () => {\n const label = bookmarkDraft?.trim();\n if (!label) { setBookmarkDraft(null); return; }\n setBookmarks(b => [...b, { label, url }]);\n setBookmarkDraft(null);\n };\n\n // Auto-focus + select the popover input when it opens, and dismiss on\n // outside click.\n useEffect(() => {\n if (bookmarkDraft === null) return;\n bookmarkInputRef.current?.focus();\n bookmarkInputRef.current?.select();\n const onDocClick = (e: MouseEvent) => {\n const target = e.target as HTMLElement | null;\n if (!target?.closest('[data-bookmark-popover]')) setBookmarkDraft(null);\n };\n // Defer one tick so the click that opened the popover doesn't close it.\n const t = setTimeout(() => document.addEventListener('mousedown', onDocClick), 0);\n return () => {\n clearTimeout(t);\n document.removeEventListener('mousedown', onDocClick);\n };\n }, [bookmarkDraft !== null]);\n\n const removeBookmark = (i: number) => {\n setBookmarks(b => b.filter((_, idx) => idx !== i));\n };\n\n const setAsHomepage = () => {\n setHomepage(url);\n try { localStorage.setItem(HOMEPAGE_KEY, url); } catch {}\n };\n\n const onSubmitUrl = (e: React.FormEvent) => {\n e.preventDefault();\n navigate(inputUrl);\n };\n\n const canBack = historyIdx > 0;\n const canForward = historyIdx < history.length - 1;\n const isBookmarked = bookmarks.some(b => b.url === url);\n const btn = 'p-1.5 rounded hover:bg-gray-200 transition-colors text-gray-600 disabled:opacity-30';\n\n return (\n <div className=\"relative flex flex-col h-full bg-white\">\n <WindowTitle title={`Browser - ${titleFromUrl(url)}`} />\n <AboutApp app=\"browser\" />\n\n {/* Top toolbar */}\n <div className=\"flex items-center gap-1 px-2 py-1.5 border-b border-gray-200 bg-gray-50 shrink-0\">\n <button onClick={back} disabled={!canBack} className={btn} title=\"Back\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M15.75 19.5L8.25 12l7.5-7.5\" /></svg>\n </button>\n <button onClick={forward} disabled={!canForward} className={btn} title=\"Forward\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8.25 4.5l7.5 7.5-7.5 7.5\" /></svg>\n </button>\n <button onClick={refresh} className={btn} title=\"Refresh\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.8}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M16.023 9.348h4.992V4.356M2.985 19.644v-4.992h4.992M3.05 9.348a9 9 0 0114.85-3.36L21.015 9.348m0 5.304a9 9 0 01-14.85 3.36l-3.115-3.36\" /></svg>\n </button>\n <button onClick={goHome} className={btn} title=\"Home\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.8}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M2.25 12l8.954-8.955a1.5 1.5 0 012.122 0l8.954 8.955M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25\" /></svg>\n </button>\n\n <form onSubmit={onSubmitUrl} className=\"flex-1 flex items-center mx-1\">\n <div className=\"flex items-center w-full bg-white border border-gray-300 rounded focus-within:border-blue-400 focus-within:ring-1 focus-within:ring-blue-200\">\n <span className=\"px-2 text-gray-400 text-xs\">{url.startsWith('https://') ? '🔒' : '⚠️'}</span>\n <input\n type=\"text\"\n value={inputUrl}\n onChange={(e) => setInputUrl(e.target.value)}\n onFocus={(e) => e.target.select()}\n className=\"flex-1 px-1 py-1 text-sm bg-transparent outline-none font-mono\"\n spellCheck={false}\n placeholder=\"Enter URL or search…\"\n />\n {url !== inputUrl && (\n <button\n type=\"button\"\n onClick={() => setInputUrl(url)}\n className=\"px-1.5 text-[10px] text-gray-400 hover:text-gray-700\"\n title=\"Reset to current URL\"\n >×</button>\n )}\n </div>\n </form>\n\n <button\n data-bookmark-popover\n onClick={() => isBookmarked\n ? removeBookmark(bookmarks.findIndex(b => b.url === url))\n : openBookmarkPopover()}\n className={btn + ' ' + (isBookmarked ? 'text-yellow-500' : '')}\n title={isBookmarked ? 'Remove bookmark' : 'Add bookmark'}\n >\n <svg className=\"h-4 w-4\" fill={isBookmarked ? 'currentColor' : 'none'} viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z\" />\n </svg>\n </button>\n <button onClick={openExternal} className={btn} title=\"Open in new tab\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-7.5-9L21 3m0 0v6m0-6L9.75 14.25\" /></svg>\n </button>\n <button onClick={() => setShowHelp(s => !s)} className={btn} title=\"Embedding help\">\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z\" /></svg>\n </button>\n </div>\n\n {/* Inline \"name this bookmark\" popover. Anchored to the right of the\n toolbar (where the star icon lives) so the visual flow matches\n how the user just clicked. Renders inside the panel so it\n inherits the window's z-stacking. */}\n {bookmarkDraft !== null && (\n <div\n data-bookmark-popover\n className=\"absolute right-3 top-12 z-30 w-72 bg-white border border-gray-200 rounded-md shadow-lg p-3\"\n >\n <div className=\"text-[11px] font-semibold uppercase tracking-wide text-gray-500 mb-1\">Bookmark this page</div>\n <div className=\"text-[11px] text-gray-400 truncate mb-2 font-mono\" title={url}>{url}</div>\n <input\n ref={bookmarkInputRef}\n type=\"text\"\n value={bookmarkDraft}\n onChange={(e) => setBookmarkDraft(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') commitBookmark();\n else if (e.key === 'Escape') setBookmarkDraft(null);\n }}\n placeholder=\"Name\"\n className=\"w-full text-sm px-2 py-1.5 border border-gray-300 rounded focus:outline-none focus:border-blue-400 focus:ring-1 focus:ring-blue-200\"\n />\n <div className=\"flex justify-end gap-1 mt-2\">\n <button\n onClick={() => setBookmarkDraft(null)}\n className=\"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded\"\n >Cancel</button>\n <button\n onClick={commitBookmark}\n disabled={!bookmarkDraft.trim()}\n className=\"px-2 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-40\"\n >Save</button>\n </div>\n </div>\n )}\n\n {/* Bookmarks bar */}\n <div className=\"flex items-center gap-0.5 px-2 py-1 border-b border-gray-200 bg-white shrink-0 overflow-x-auto\">\n {bookmarks.length === 0 && (\n <span className=\"text-[11px] text-gray-400 italic px-2\">No bookmarks yet — star the address bar to add one.</span>\n )}\n {bookmarks.map((b, i) => (\n <button\n key={i}\n onClick={() => navigate(b.url)}\n onContextMenu={async (e) => {\n e.preventDefault();\n const ok = await confirm({\n title: 'Remove bookmark',\n message: `\"${b.label}\" will be removed from your bookmarks.`,\n confirmLabel: 'Remove',\n variant: 'danger',\n });\n if (ok) removeBookmark(i);\n }}\n className=\"flex items-center gap-1.5 px-2 py-0.5 rounded text-[12px] text-gray-700 hover:bg-gray-100 whitespace-nowrap\"\n title={`${b.url}\\n(right-click to remove)`}\n >\n <img\n src={`https://icons.duckduckgo.com/ip3/${new URL(b.url).hostname}.ico`}\n alt=\"\"\n className=\"h-3.5 w-3.5\"\n onError={(e) => { (e.target as HTMLImageElement).style.visibility = 'hidden'; }}\n />\n {b.label}\n </button>\n ))}\n <div className=\"flex-1\" />\n <button\n onClick={setAsHomepage}\n className=\"px-2 py-0.5 text-[11px] text-gray-500 hover:text-gray-800\"\n title=\"Set current page as homepage\"\n >Set as home</button>\n </div>\n\n {/* Iframe area */}\n <div className=\"flex-1 relative min-h-0 bg-gray-50\">\n <BrowserBody\n url={url}\n iframeKey={iframeKey}\n iframeRef={iframeRef}\n openExternal={openExternal}\n showHelp={showHelp}\n dismissHelp={() => setShowHelp(false)}\n />\n </div>\n </div>\n );\n}\n\nfunction titleFromUrl(u: string): string {\n try {\n const url = new URL(u);\n return url.hostname.replace(/^www\\./, '');\n } catch {\n return u;\n }\n}\n\nfunction BrowserBody({\n url,\n iframeKey,\n iframeRef,\n openExternal,\n showHelp,\n dismissHelp,\n}: {\n url: string;\n iframeKey: number;\n iframeRef: React.RefObject<HTMLIFrameElement | null>;\n openExternal: () => void;\n showHelp: boolean;\n dismissHelp: () => void;\n}) {\n // If the user dismisses the blocked-site panel, allow them to attempt\n // the iframe load anyway. Reset the override whenever URL changes.\n const [forceTry, setForceTry] = useState(false);\n useEffect(() => { setForceTry(false); }, [url]);\n\n const blocked = hostIsBlocked(url) && !forceTry;\n\n if (blocked) {\n return (\n <div className=\"absolute inset-0 flex items-center justify-center bg-white p-8\">\n <div className=\"max-w-md text-center\">\n <svg className=\"h-14 w-14 mx-auto text-gray-300 mb-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.2}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L21 21M5.636 5.636L3 3m9 9a9 9 0 110-18 9 9 0 010 18z\" />\n </svg>\n <h3 className=\"text-base font-semibold text-gray-800 mb-1\">\n {titleFromUrl(url)} can't be embedded\n </h3>\n <p className=\"text-sm text-gray-500 mb-4\">\n This site sends an <span className=\"font-mono text-xs\">X-Frame-Options</span>{' '}\n or <span className=\"font-mono text-xs\">Content-Security-Policy</span> header\n that refuses iframe embedding. The browser blocks the load before our app\n can do anything about it.\n </p>\n <div className=\"flex flex-col items-center gap-2\">\n <button\n onClick={openExternal}\n className=\"px-4 py-2 bg-blue-500 text-white text-sm rounded hover:bg-blue-600 inline-flex items-center gap-2\"\n >\n <svg className=\"h-4 w-4\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={1.5}>\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-7.5-9L21 3m0 0v6m0-6L9.75 14.25\" />\n </svg>\n Open in a new tab\n </button>\n <button\n onClick={() => setForceTry(true)}\n className=\"text-xs text-gray-500 hover:text-gray-800 underline\"\n >\n Try loading it here anyway\n </button>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <>\n <iframe\n key={iframeKey + url}\n ref={iframeRef as React.RefObject<HTMLIFrameElement>}\n src={url}\n className=\"absolute inset-0 w-full h-full bg-white\"\n // Sandboxing keeps embedded pages from messing with the parent\n // window state. allow-same-origin lets sites that *do* allow\n // embedding actually behave normally; allow-scripts is needed\n // for any modern site.\n sandbox=\"allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-modals\"\n referrerPolicy=\"no-referrer-when-downgrade\"\n />\n\n {showHelp && (\n <div className=\"absolute top-2 right-2 max-w-sm bg-white border border-gray-200 rounded-md shadow-lg p-3 text-xs text-gray-700 z-10\">\n <div className=\"font-medium text-gray-900 mb-1\">Why is this page blank?</div>\n <p className=\"mb-2\">\n Most major sites (Google, GitHub, banks, news) refuse to be embedded in\n an iframe via <span className=\"font-mono\">X-Frame-Options</span> or\n Content Security Policy. There's no workaround — the browser blocks the\n load before our app can do anything.\n </p>\n <p>\n Hit the <span className=\"font-medium\">↗</span> button to open the page in\n a real new tab. Sites that <em>do</em> allow embedding (Wikipedia, DevDocs,\n your own apps) work fine in here.\n </p>\n <button\n onClick={dismissHelp}\n className=\"mt-2 text-[11px] text-blue-600 hover:underline\"\n >Got it</button>\n </div>\n )}\n </>\n );\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/apps/Calculator.tsx"],"names":[],"mappings":";;;;;;AAKA,IAAM,iBAAA,GAAoB,iBAAA;AAEX,SAAR,UAAA,GAA8B;AACnC,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,SAAS,GAAG,CAAA;AAC1C,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAwB,IAAI,CAAA;AACpD,EAAA,MAAM,CAAC,EAAA,EAAI,KAAK,CAAA,GAAI,SAAa,IAAI,CAAA;AACrC,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAChD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,EAAE,CAAA;AAC3C,EAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAI,QAAA,CAAmB,EAAE,CAAA;AACnD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,KAAK,CAAA;AAC1C,EAAA,MAAM,CAAC,YAAY,aAAa,CAAA,GAAI,SAAS,MAAM,cAAA,CAAe,iBAAiB,CAAC,CAAA;AACpF,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAS,KAAK,CAAA;AACtD,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,SAA2B,UAAU,CAAA;AAErF,EAAA,iBAAA,CAAkB,YAAY,MAAM;AAClC,IAAA,mBAAA,CAAoB,EAAE,GAAG,UAAA,EAAY,CAAA;AACrC,IAAA,eAAA,CAAgB,IAAI,CAAA;AAAA,EACtB,CAAA,EAAG,CAAC,UAAU,CAAC,CAAC,CAAA;AAEhB,EAAA,MAAM,MAAA,GAAS,WAAA,CAAY,CAAC,EAAA,KAAe;AACzC,IAAA,WAAA,CAAY,EAAE,CAAA;AACd,IAAA,UAAA,CAAW,CAAA,CAAA,KAAK;AACd,MAAA,IAAI,SAAA,IAAa,MAAM,GAAA,EAAK;AAC1B,QAAA,YAAA,CAAa,KAAK,CAAA;AAClB,QAAA,OAAO,EAAA,KAAO,MAAM,IAAA,GAAO,EAAA;AAAA,MAC7B;AACA,MAAA,IAAI,OAAO,GAAA,IAAO,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,OAAO,CAAA;AAC1C,MAAA,OAAO,CAAA,GAAI,EAAA;AAAA,IACb,CAAC,CAAA;AAAA,EACH,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAEd,EAAA,MAAM,KAAA,GAAQ,YAAY,MAAM;AAC9B,IAAA,UAAA,CAAW,GAAG,CAAA;AACd,IAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,IAAA,KAAA,CAAM,IAAI,CAAA;AACV,IAAA,YAAA,CAAa,KAAK,CAAA;AAAA,EACpB,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,CAAC,CAAA,EAAW,GAAW,QAAA,KAAyB;AAC1E,IAAA,QAAQ,QAAA;AAAU,MAChB,KAAK,GAAA;AAAK,QAAA,OAAO,CAAA,GAAI,CAAA;AAAA,MACrB,KAAK,GAAA;AAAK,QAAA,OAAO,CAAA,GAAI,CAAA;AAAA,MACrB,KAAK,MAAA;AAAK,QAAA,OAAO,CAAA,GAAI,CAAA;AAAA,MACrB,KAAK,MAAA;AAAK,QAAA,OAAO,CAAA,KAAM,CAAA,GAAI,CAAA,GAAI,CAAA,GAAI,CAAA;AAAA,MACnC;AAAS,QAAA,OAAO,CAAA;AAAA;AAClB,EACF,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,QAAA,GAAW,WAAA,CAAY,CAAC,MAAA,KAAe;AAC3C,IAAA,MAAM,OAAA,GAAU,WAAW,OAAO,CAAA;AAClC,IAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,EAAA,IAAM,CAAC,SAAA,EAAW;AACrC,MAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,IAAA,EAAM,OAAA,EAAS,EAAE,CAAA;AACxC,MAAA,UAAA,CAAW,MAAA,CAAO,MAAM,CAAC,CAAA;AACzB,MAAA,OAAA,CAAQ,MAAM,CAAA;AACd,MAAA,UAAA,CAAW,OAAK,CAAC,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,EAAE,CAAA,CAAA,EAAI,OAAO,CAAA,GAAA,EAAM,MAAM,IAAI,GAAG,CAAC,EAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA;AAAA,IAC7E,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,OAAO,CAAA;AAAA,IACjB;AACA,IAAA,KAAA,CAAM,MAAM,CAAA;AACZ,IAAA,YAAA,CAAa,IAAI,CAAA;AAAA,EACnB,GAAG,CAAC,OAAA,EAAS,MAAM,EAAA,EAAI,SAAA,EAAW,OAAO,CAAC,CAAA;AAE1C,EAAA,MAAM,MAAA,GAAS,YAAY,MAAM;AAC/B,IAAA,IAAI,IAAA,KAAS,IAAA,IAAQ,CAAC,EAAA,EAAI;AAC1B,IAAA,MAAM,OAAA,GAAU,WAAW,OAAO,CAAA;AAClC,IAAA,MAAM,MAAA,GAAS,OAAA,CAAQ,IAAA,EAAM,OAAA,EAAS,EAAE,CAAA;AACxC,IAAA,MAAM,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,EAAE,IAAI,OAAO,CAAA,EAAA,CAAA;AACrC,IAAA,UAAA,CAAW,CAAA,CAAA,KAAK,CAAC,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI,GAAG,CAAC,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA;AACxD,IAAA,WAAA,CAAY,IAAI,CAAA;AAChB,IAAA,UAAA,CAAW,MAAA,CAAO,MAAM,CAAC,CAAA;AACzB,IAAA,OAAA,CAAQ,IAAI,CAAA;AACZ,IAAA,KAAA,CAAM,IAAI,CAAA;AACV,IAAA,YAAA,CAAa,IAAI,CAAA;AAAA,EACnB,GAAG,CAAC,OAAA,EAAS,IAAA,EAAM,EAAA,EAAI,OAAO,CAAC,CAAA;AAE/B,EAAA,MAAM,OAAA,GAAU,YAAY,MAAM;AAChC,IAAA,UAAA,CAAW,OAAK,MAAA,CAAO,UAAA,CAAW,CAAC,CAAA,GAAI,GAAG,CAAC,CAAA;AAAA,EAC7C,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,MAAA,GAAS,YAAY,MAAM;AAC/B,IAAA,UAAA,CAAW,CAAA,CAAA,KAAK,CAAA,CAAE,UAAA,CAAW,GAAG,CAAA,GAAI,CAAA,CAAE,KAAA,CAAM,CAAC,CAAA,GAAI,CAAA,KAAM,GAAA,GAAM,CAAA,GAAI,MAAM,CAAC,CAAA;AAAA,EAC1E,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,OAAA,GAAU,CAAC,CAAA,KAAqB;AACpC,MAAA,MAAM,GAAA,GAAO,EAAE,MAAA,CAAuB,OAAA;AACtC,MAAA,IAAI,GAAA,KAAQ,OAAA,IAAW,GAAA,KAAQ,UAAA,IAAc,QAAQ,QAAA,EAAU;AAC/D,MAAA,IAAI,CAAA,CAAE,OAAO,GAAA,IAAO,CAAA,CAAE,OAAO,GAAA,EAAK,MAAA,CAAO,EAAE,GAAG,CAAA;AAAA,WAAA,IACrC,CAAA,CAAE,GAAA,KAAQ,GAAA,EAAK,MAAA,CAAO,GAAG,CAAA;AAAA,WAAA,IACzB,CAAA,CAAE,GAAA,KAAQ,GAAA,EAAK,QAAA,CAAS,GAAG,CAAA;AAAA,WAAA,IAC3B,CAAA,CAAE,GAAA,KAAQ,GAAA,EAAK,QAAA,CAAS,GAAG,CAAA;AAAA,WAAA,IAC3B,CAAA,CAAE,GAAA,KAAQ,GAAA,EAAK,QAAA,CAAS,MAAG,CAAA;AAAA,WAAA,IAC3B,CAAA,CAAE,QAAQ,GAAA,EAAK;AAAE,QAAA,CAAA,CAAE,cAAA,EAAe;AAAG,QAAA,QAAA,CAAS,MAAG,CAAA;AAAA,MAAG,WACpD,CAAA,CAAE,GAAA,KAAQ,WAAW,CAAA,CAAE,GAAA,KAAQ,KAAK,MAAA,EAAO;AAAA,WAAA,IAC3C,CAAA,CAAE,GAAA,KAAQ,QAAA,EAAU,KAAA,EAAM;AAAA,WAAA,IAC1B,CAAA,CAAE,GAAA,KAAQ,WAAA,EAAa,UAAA,CAAW,CAAA,CAAA,KAAK,CAAA,CAAE,MAAA,GAAS,CAAA,GAAI,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,EAAE,IAAI,GAAG,CAAA;AAAA,WAAA,IAC1E,CAAA,CAAE,GAAA,KAAQ,GAAA,EAAK,OAAA,EAAQ;AAAA,IAClC,CAAA;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,OAAO,CAAA;AAC1C,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,OAAO,CAAA;AAAA,EAC5D,GAAG,CAAC,MAAA,EAAQ,UAAU,MAAA,EAAQ,KAAA,EAAO,OAAO,CAAC,CAAA;AAE7C,EAAA,MAAM,GAAA,GAAM,mFAAA;AACZ,EAAA,MAAM,MAAA,GAAS,GAAG,GAAG,CAAA,kFAAA,CAAA;AACrB,EAAA,MAAM,KAAA,GAAQ,GAAG,GAAG,CAAA,qFAAA,CAAA;AACpB,EAAA,MAAM,KAAA,GAAQ,GAAG,GAAG,CAAA,sFAAA,CAAA;AACpB,EAAA,MAAM,KAAA,GAAQ,GAAG,GAAG,CAAA,4DAAA,CAAA;AAGpB,EAAA,MAAM,aAAA,GAAgB,CAAC,GAAA,KAAgB;AACrC,IAAA,MAAM,GAAA,GAAM,WAAW,GAAG,CAAA;AAC1B,IAAA,IAAI,KAAA,CAAM,GAAG,CAAA,EAAG,OAAO,GAAA;AACvB,IAAA,IAAI,GAAA,CAAI,SAAS,GAAG,CAAA,IAAK,IAAI,QAAA,CAAS,IAAI,GAAG,OAAO,GAAA;AACpD,IAAA,IAAI,IAAA,CAAK,IAAI,GAAG,CAAA,GAAI,QAAQ,GAAA,CAAI,MAAA,GAAS,IAAI,OAAO,GAAA;AACpD,IAAA,OAAO,GAAA,CAAI,cAAc,CAAC,CAAA;AAAA,EAC5B,CAAA;AAEA,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACA,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,sBAAA,EAAuB,KAAA,EAAO;AAAA,MAC3C,OAAA,EAAS,WAAW,aAAA,GAAgB,GAAA;AAAA,MACpC,gBAAgB,UAAA,CAAW,UAAA,GAAa,IAAI,CAAA,KAAA,EAAQ,UAAA,CAAW,UAAU,CAAA,GAAA,CAAA,GAAQ;AAAA,KACnF,EAEE,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,8HAAA,EAEb,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,kDAAA,EACZ,QAAA,EAAA,OAAA,CAAQ,MAAM,CAAA,EAAG,CAAC,EAAE,OAAA,EAAQ,CAAE,IAAI,CAAC,CAAA,EAAG,sBACrC,GAAA,CAAC,KAAA,EAAA,EAAY,WAAU,0DAAA,EAA4D,QAAA,EAAA,CAAA,EAAA,EAAzE,CAA2E,CACtF,CAAA,EACH,CAAA;AAAA,QAAA,CAEE,YAAa,EAAA,IAAM,IAAA,KAAS,yBAC5B,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,+CAAA,EACZ,QAAA,EAAA,QAAA,IAAY,CAAA,EAAG,IAAI,IAAI,EAAE,CAAA,CAAA,EAAI,CAAC,SAAA,GAAY,OAAA,GAAU,EAAE,CAAA,CAAA,EACzD,CAAA;AAAA,wBAGF,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yBAAA,EACb,QAAA,EAAA;AAAA,0BAAA,GAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cAAO,SAAS,MAAM;AAAE,gBAAA,SAAA,CAAU,SAAA,CAAU,UAAU,OAAO,CAAA;AAAG,gBAAA,SAAA,CAAU,IAAI,CAAA;AAAG,gBAAA,UAAA,CAAW,MAAM,SAAA,CAAU,KAAK,CAAA,EAAG,IAAI,CAAA;AAAA,cAAG,CAAA;AAAA,cAC1H,KAAA,EAAM,aAAA;AAAA,cACN,SAAA,EAAU,0GAAA;AAAA,cACT,mCACG,GAAA,CAAC,KAAA,EAAA,EAAI,WAAU,wBAAA,EAAyB,IAAA,EAAK,QAAO,OAAA,EAAQ,WAAA,EAAY,QAAO,cAAA,EAAe,WAAA,EAAa,GAAG,QAAA,kBAAA,GAAA,CAAC,MAAA,EAAA,EAAK,eAAc,OAAA,EAAQ,cAAA,EAAe,SAAQ,CAAA,EAAE,gBAAA,EAAiB,GAAE,CAAA,mBACtL,GAAA,CAAC,SAAI,SAAA,EAAU,SAAA,EAAU,MAAK,MAAA,EAAO,OAAA,EAAQ,aAAY,MAAA,EAAO,cAAA,EAAe,aAAa,CAAA,EAAG,QAAA,kBAAA,GAAA,CAAC,UAAK,aAAA,EAAc,OAAA,EAAQ,gBAAe,OAAA,EAAQ,CAAA,EAAE,yHAAwH,CAAA,EAAE;AAAA;AAAA,WAEpR;AAAA,8BACC,KAAA,EAAA,EAAI,SAAA,EAAU,wEAAA,EACZ,QAAA,EAAA,aAAA,CAAc,OAAO,CAAA,EACxB;AAAA,SAAA,EACF;AAAA,OAAA,EACF,CAAA;AAAA,sBAGA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,yDAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,KAAA,EAAO,SAAA,EAAW,OAAO,QAAA,EAAA,IAAA,EAAE,CAAA;AAAA,4BAC3C,QAAA,EAAA,EAAO,OAAA,EAAS,MAAA,EAAQ,SAAA,EAAW,OAAO,QAAA,EAAA,UAAA,EAAG,CAAA;AAAA,4BAC7C,QAAA,EAAA,EAAO,OAAA,EAAS,OAAA,EAAS,SAAA,EAAW,OAAO,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,4BAC5C,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,QAAA,CAAS,MAAG,CAAA,EAAG,SAAA,EAAW,CAAA,EAAG,KAAK,IAAI,EAAA,KAAO,MAAA,GAAM,sBAAA,GAAyB,EAAE,IAAI,QAAA,EAAA,MAAA,EAAC,CAAA;AAAA,wBAE1G,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,wBACxD,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,wBACxD,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,4BACvD,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,QAAA,CAAS,MAAG,CAAA,EAAG,SAAA,EAAW,CAAA,EAAG,KAAK,IAAI,EAAA,KAAO,MAAA,GAAM,sBAAA,GAAyB,EAAE,IAAI,QAAA,EAAA,MAAA,EAAC,CAAA;AAAA,wBAE1G,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,wBACxD,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,wBACxD,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,4BACvD,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,QAAA,CAAS,GAAG,CAAA,EAAG,SAAA,EAAW,CAAA,EAAG,KAAK,IAAI,EAAA,KAAO,GAAA,GAAM,sBAAA,GAAyB,EAAE,IAAI,QAAA,EAAA,QAAA,EAAC,CAAA;AAAA,wBAE1G,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,wBACxD,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,wBACxD,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,4BACvD,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,QAAA,CAAS,GAAG,CAAA,EAAG,SAAA,EAAW,CAAA,EAAG,KAAK,IAAI,EAAA,KAAO,GAAA,GAAM,sBAAA,GAAyB,EAAE,IAAI,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,wBAE1G,GAAA,CAAC,QAAA,EAAA,EAAO,OAAA,EAAS,MAAM,MAAA,CAAO,GAAG,CAAA,EAAG,SAAA,EAAW,CAAA,EAAG,MAAM,CAAA,WAAA,CAAA,EAAe,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,wBACxE,GAAA,CAAC,YAAO,OAAA,EAAS,MAAM,OAAO,GAAG,CAAA,EAAG,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAA,GAAA,EAAC,CAAA;AAAA,4BACvD,QAAA,EAAA,EAAO,OAAA,EAAS,MAAA,EAAQ,SAAA,EAAW,OAAO,QAAA,EAAA,GAAA,EAAC;AAAA,OAAA,EAC9C;AAAA,KAAA,EAEF,CAAA;AAAA,oBACA,GAAA;AAAA,MAAC,mBAAA;AAAA,MAAA;AAAA,QAAoB,IAAA,EAAM,YAAA;AAAA,QAAc,OAAA,EAAS,MAAM,eAAA,CAAgB,KAAK,CAAA;AAAA,QAAG,KAAA,EAAM,qBAAA;AAAA,QACpF,UAAA,EAAY,gBAAA;AAAA,QAAkB,kBAAA,EAAoB,mBAAA;AAAA,QAClD,QAAQ,MAAM;AAAE,UAAA,aAAA,CAAc,gBAAgB,CAAA;AAAG,UAAA,YAAA,CAAa,OAAA,CAAQ,iBAAA,EAAmB,IAAA,CAAK,SAAA,CAAU,gBAAgB,CAAC,CAAA;AAAG,UAAA,eAAA,CAAgB,KAAK,CAAA;AAAA,QAAG;AAAA;AAAA;AAAG,GAAA,EACzJ,CAAA;AAEJ","file":"Calculator-FFCYNYU3.js","sourcesContent":["import { useState, useCallback, useEffect } from 'react';\nimport { useWidgetSettings } from '../shell/Modal';\nimport WidgetSettingsModal, { loadAppearance, type WidgetAppearance } from '../shell/WidgetSettingsModal';\n\ntype Op = '+' | '-' | '×' | '÷' | null;\nconst CALC_SETTINGS_KEY = 'calc_appearance';\n\nexport default function Calculator() {\n const [display, setDisplay] = useState('0');\n const [prev, setPrev] = useState<number | null>(null);\n const [op, setOp] = useState<Op>(null);\n const [resetNext, setResetNext] = useState(false);\n const [lastExpr, setLastExpr] = useState('');\n const [history, setHistory] = useState<string[]>([]);\n const [copied, setCopied] = useState(false);\n const [appearance, setAppearance] = useState(() => loadAppearance(CALC_SETTINGS_KEY));\n const [settingsOpen, setSettingsOpen] = useState(false);\n const [configAppearance, setConfigAppearance] = useState<WidgetAppearance>(appearance);\n\n useWidgetSettings(useCallback(() => {\n setConfigAppearance({ ...appearance });\n setSettingsOpen(true);\n }, [appearance]));\n\n const append = useCallback((ch: string) => {\n setLastExpr('');\n setDisplay(d => {\n if (resetNext || d === '0') {\n setResetNext(false);\n return ch === '.' ? '0.' : ch;\n }\n if (ch === '.' && d.includes('.')) return d;\n return d + ch;\n });\n }, [resetNext]);\n\n const clear = useCallback(() => {\n setDisplay('0');\n setPrev(null);\n setOp(null);\n setResetNext(false);\n }, []);\n\n const compute = useCallback((a: number, b: number, operator: Op): number => {\n switch (operator) {\n case '+': return a + b;\n case '-': return a - b;\n case '×': return a * b;\n case '÷': return b === 0 ? 0 : a / b;\n default: return b;\n }\n }, []);\n\n const handleOp = useCallback((nextOp: Op) => {\n const current = parseFloat(display);\n if (prev !== null && op && !resetNext) {\n const result = compute(prev, current, op);\n setDisplay(String(result));\n setPrev(result);\n setHistory(h => [`${prev} ${op} ${current} = ${result}`, ...h].slice(0, 20));\n } else {\n setPrev(current);\n }\n setOp(nextOp);\n setResetNext(true);\n }, [display, prev, op, resetNext, compute]);\n\n const equals = useCallback(() => {\n if (prev === null || !op) return;\n const current = parseFloat(display);\n const result = compute(prev, current, op);\n const expr = `${prev} ${op} ${current} =`;\n setHistory(h => [`${expr} ${result}`, ...h].slice(0, 20));\n setLastExpr(expr);\n setDisplay(String(result));\n setPrev(null);\n setOp(null);\n setResetNext(true);\n }, [display, prev, op, compute]);\n\n const percent = useCallback(() => {\n setDisplay(d => String(parseFloat(d) / 100));\n }, []);\n\n const negate = useCallback(() => {\n setDisplay(d => d.startsWith('-') ? d.slice(1) : d === '0' ? d : '-' + d);\n }, []);\n\n // Keyboard support\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n const tag = (e.target as HTMLElement).tagName;\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return;\n if (e.key >= '0' && e.key <= '9') append(e.key);\n else if (e.key === '.') append('.');\n else if (e.key === '+') handleOp('+');\n else if (e.key === '-') handleOp('-');\n else if (e.key === '*') handleOp('×');\n else if (e.key === '/') { e.preventDefault(); handleOp('÷'); }\n else if (e.key === 'Enter' || e.key === '=') equals();\n else if (e.key === 'Escape') clear();\n else if (e.key === 'Backspace') setDisplay(d => d.length > 1 ? d.slice(0, -1) : '0');\n else if (e.key === '%') percent();\n };\n window.addEventListener('keydown', handler);\n return () => window.removeEventListener('keydown', handler);\n }, [append, handleOp, equals, clear, percent]);\n\n const btn = 'flex items-center justify-center rounded-lg text-sm font-medium transition-colors';\n const numBtn = `${btn} bg-white border border-gray-200 text-gray-900 hover:bg-gray-50 active:bg-gray-100`;\n const opBtn = `${btn} bg-blue-50 border border-blue-200 text-blue-700 hover:bg-blue-100 active:bg-blue-200`;\n const fnBtn = `${btn} bg-gray-100 border border-gray-200 text-gray-600 hover:bg-gray-200 active:bg-gray-300`;\n const eqBtn = `${btn} bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800`;\n\n // Format display for readability\n const formatDisplay = (val: string) => {\n const num = parseFloat(val);\n if (isNaN(num)) return val;\n if (val.endsWith('.') || val.endsWith('.0')) return val;\n if (Math.abs(num) < 1e15 && val.length < 16) return val;\n return num.toExponential(6);\n };\n\n return (\n <>\n <div className=\"flex flex-col h-full\" style={{\n opacity: appearance.activeOpacity / 100,\n backdropFilter: appearance.activeBlur > 0 ? `blur(${appearance.activeBlur}px)` : undefined,\n }}>\n {/* Display — 5-line area with history + current value */}\n <div className=\"bg-slate-200 rounded-t-lg px-4 py-2 group/display relative flex flex-col justify-end min-h-[120px] border-b border-slate-300\">\n {/* History lines (most recent at bottom, above current) */}\n <div className=\"flex flex-col justify-end flex-1 overflow-hidden\">\n {history.slice(0, 3).reverse().map((h, i) => (\n <div key={i} className=\"text-[11px] text-slate-400 text-right font-mono truncate\">{h}</div>\n ))}\n </div>\n {/* Current expression */}\n {(lastExpr || (op && prev !== null)) && (\n <div className=\"text-base text-slate-600 text-right font-mono\">\n {lastExpr || `${prev} ${op} ${!resetNext ? display : ''}`}\n </div>\n )}\n {/* Current value */}\n <div className=\"flex items-center gap-2\">\n <button onClick={() => { navigator.clipboard.writeText(display); setCopied(true); setTimeout(() => setCopied(false), 1200); }}\n title=\"Copy result\"\n className=\"shrink-0 text-slate-300 hover:text-slate-500 transition-colors opacity-0 group-hover/display:opacity-100\">\n {copied\n ? <svg className=\"h-5 w-5 text-green-500\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M5 13l4 4L19 7\" /></svg>\n : <svg className=\"h-5 w-5\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" strokeWidth={2}><path strokeLinecap=\"round\" strokeLinejoin=\"round\" d=\"M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z\" /></svg>\n }\n </button>\n <div className=\"text-right text-4xl font-mono font-bold text-slate-800 truncate flex-1\">\n {formatDisplay(display)}\n </div>\n </div>\n </div>\n\n {/* Buttons */}\n <div className=\"grid grid-cols-4 grid-rows-5 gap-1.5 flex-1 min-h-0 p-2\">\n <button onClick={clear} className={fnBtn}>AC</button>\n <button onClick={negate} className={fnBtn}>+/−</button>\n <button onClick={percent} className={fnBtn}>%</button>\n <button onClick={() => handleOp('÷')} className={`${opBtn} ${op === '÷' ? 'ring-2 ring-blue-400' : ''}`}>÷</button>\n\n <button onClick={() => append('7')} className={numBtn}>7</button>\n <button onClick={() => append('8')} className={numBtn}>8</button>\n <button onClick={() => append('9')} className={numBtn}>9</button>\n <button onClick={() => handleOp('×')} className={`${opBtn} ${op === '×' ? 'ring-2 ring-blue-400' : ''}`}>×</button>\n\n <button onClick={() => append('4')} className={numBtn}>4</button>\n <button onClick={() => append('5')} className={numBtn}>5</button>\n <button onClick={() => append('6')} className={numBtn}>6</button>\n <button onClick={() => handleOp('-')} className={`${opBtn} ${op === '-' ? 'ring-2 ring-blue-400' : ''}`}>−</button>\n\n <button onClick={() => append('1')} className={numBtn}>1</button>\n <button onClick={() => append('2')} className={numBtn}>2</button>\n <button onClick={() => append('3')} className={numBtn}>3</button>\n <button onClick={() => handleOp('+')} className={`${opBtn} ${op === '+' ? 'ring-2 ring-blue-400' : ''}`}>+</button>\n\n <button onClick={() => append('0')} className={`${numBtn} col-span-2`}>0</button>\n <button onClick={() => append('.')} className={numBtn}>.</button>\n <button onClick={equals} className={eqBtn}>=</button>\n </div>\n\n </div>\n <WidgetSettingsModal open={settingsOpen} onClose={() => setSettingsOpen(false)} title=\"Calculator Settings\"\n appearance={configAppearance} onAppearanceChange={setConfigAppearance}\n onSave={() => { setAppearance(configAppearance); localStorage.setItem(CALC_SETTINGS_KEY, JSON.stringify(configAppearance)); setSettingsOpen(false); }} />\n </>\n );\n}\n"]}