efficient-shell 4.168.0 → 4.173.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 (82) hide show
  1. package/README.md +406 -9
  2. package/dist/{Browser-PZPREN6H.js → Browser-X7UTQJOX.js} +13 -13
  3. package/dist/Calculator-IAJLLX67.js +29 -0
  4. package/dist/ConfirmDialog-EBZ5LHVS.js +1 -0
  5. package/dist/{CurrencyConverter-3BTIZ2LT.js → CurrencyConverter-RODHLQMG.js} +5 -5
  6. package/dist/{Documents-YAOM3L47.js → Documents-BJFZHHBZ.js} +8 -8
  7. package/dist/{Files-2UHYDMLL.js → Files-PL5NE4PQ.js} +27 -27
  8. package/dist/{Notepad-OWL75AYX.js → Notepad-52LDWEK2.js} +10 -10
  9. package/dist/PdfViewer-IIXXT7SF.js +1 -0
  10. package/dist/{PomodoroTimer-SCPEOG6W.js → PomodoroTimer-C3KCTCMB.js} +30 -30
  11. package/dist/{Preview-5R3QZSJU.js → Preview-O3N26GG6.js} +31 -31
  12. package/dist/{Sidebar-C5RXL72L.js → Sidebar-QNGQA6J5.js} +24 -24
  13. package/dist/Spreadsheet-HVKVNQN6.js +37 -0
  14. package/dist/{Stock-UGDIALNB.js → Stock-YOGVYL6F.js} +6 -6
  15. package/dist/{Weather-CR2MN5XF.js → Weather-4OQOBM4C.js} +13 -13
  16. package/dist/{WorldClock-JBNJ7C7J.js → WorldClock-K5KLOOD7.js} +5 -5
  17. package/dist/apps/index.js +1 -1
  18. package/dist/bin/ui-check.js +1 -1
  19. package/dist/brand.css +1 -1
  20. package/dist/chunk-6TDPY7WG.js +3 -0
  21. package/dist/{chunk-SOI4LR67.js → chunk-A6ZY4XSQ.js} +7 -7
  22. package/dist/{chunk-4P3IWZUC.js → chunk-AYZ4LP2A.js} +9 -9
  23. package/dist/{chunk-A7Q4BCPI.js → chunk-CQN5CFKA.js} +6 -6
  24. package/dist/chunk-EXXHI5DO.js +12 -0
  25. package/dist/{chunk-M4DB7UBZ.js → chunk-FIGVDYJR.js} +16 -16
  26. package/dist/chunk-FWOPZB6I.js +1 -0
  27. package/dist/{chunk-XKHAP6N2.js → chunk-MFR4GZSB.js} +15 -15
  28. package/dist/{chunk-JLPJBO7K.js → chunk-MVUYJAZT.js} +1 -1
  29. package/dist/{chunk-YWXYBJ5Y.js → chunk-MYNLN25H.js} +2 -2
  30. package/dist/{chunk-KSSAVLO7.js → chunk-OZMDKNVR.js} +1 -1
  31. package/dist/{chunk-N3ZXQE4M.js → chunk-PGVUO7GB.js} +5 -5
  32. package/dist/chunk-RN5V7MDO.js +995 -0
  33. package/dist/chunk-S5QF4VFG.js +1 -0
  34. package/dist/{chunk-5J2TKKGH.js → chunk-SXRJYJ7M.js} +1 -1
  35. package/dist/{chunk-QZG72OVX.js → chunk-TLYGAHZN.js} +1 -1
  36. package/dist/{chunk-CUX4XGLS.js → chunk-U2SEGSNG.js} +1 -1
  37. package/dist/{chunk-FZRQBHTK.js → chunk-U6IUYOYS.js} +1 -1
  38. package/dist/{chunk-3U47BTEV.js → chunk-UOZMVS4Y.js} +11 -11
  39. package/dist/{chunk-S2AZZGFF.js → chunk-W32SKK3D.js} +1 -1
  40. package/dist/chunk-W33ZFDRR.js +1 -0
  41. package/dist/chunk-X5LH3UOJ.js +112 -0
  42. package/dist/{chunk-6MBERZOS.js → chunk-XHWT4DH2.js} +3 -3
  43. package/dist/chunk-XJX3NA37.js +1 -0
  44. package/dist/chunk-YKZHOXI4.js +2 -0
  45. package/dist/{chunk-FVXBJGY5.js → chunk-Z3JO7MDE.js} +5 -5
  46. package/dist/{chunk-PLX2N4N6.js → chunk-ZDCOC4IH.js} +9 -9
  47. package/dist/file-intake/index.js +1 -1
  48. package/dist/format/index.d.ts +1 -1
  49. package/dist/format/index.js +1 -1
  50. package/dist/icons/efficient.d.ts +355 -0
  51. package/dist/icons/efficient.js +470 -0
  52. package/dist/icons/heroicons.d.ts +6 -0
  53. package/dist/icons/heroicons.js +1 -0
  54. package/dist/icons/index.d.ts +64 -0
  55. package/dist/icons/index.js +2 -0
  56. package/dist/icons/lucide.d.ts +6 -0
  57. package/dist/icons/lucide.js +1 -0
  58. package/dist/{index-CbLbd__n.d.ts → index-Dod08dsC.d.ts} +302 -11
  59. package/dist/{index-DCUzlKPZ.d.ts → index-DpUDK9zG.d.ts} +5 -0
  60. package/dist/index.d.ts +16 -6
  61. package/dist/index.js +650 -645
  62. package/dist/map/index.js +3 -3
  63. package/dist/page-tabs/index.js +5 -5
  64. package/dist/pdf/index.js +1 -1
  65. package/dist/registry-FHdUeVww.d.ts +71 -0
  66. package/dist/sounds-FO772MN2.js +1 -0
  67. package/dist/ui/index.d.ts +4 -2
  68. package/dist/ui/index.js +1 -1
  69. package/dist/ui.css +81 -0
  70. package/package.json +27 -1
  71. package/dist/Calculator-NXMUEH5D.js +0 -29
  72. package/dist/ConfirmDialog-ZDVMT74Y.js +0 -1
  73. package/dist/PdfViewer-S2B2DZYB.js +0 -1
  74. package/dist/Spreadsheet-XT4HF7MQ.js +0 -37
  75. package/dist/chunk-6KO2U66R.js +0 -3
  76. package/dist/chunk-7U3F53IG.js +0 -1
  77. package/dist/chunk-BUTSU7FO.js +0 -1
  78. package/dist/chunk-GVZLCY3B.js +0 -994
  79. package/dist/chunk-JYLI2WXW.js +0 -12
  80. package/dist/chunk-LW27IM3S.js +0 -112
  81. package/dist/chunk-NCQLGQYR.js +0 -1
  82. package/dist/sounds-OMHXVVQ4.js +0 -1
package/README.md CHANGED
@@ -965,10 +965,11 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
965
965
  | `<ShellAuthProvider value={{ hasAnyPerm }}>` | Permission-filter nav items. |
966
966
  | `<ShellPrefsProvider value={{ prefs, save }}>` | Where the shell reads/writes user prefs (theme, taskbar pos, sticky notes, …). Use `useLocalStoragePrefs(key)` for a backend-less default. Since 4.92.0 it is also how `<EntityList>` / `<ResizableTable>` column config and sort persist: with no provider mounted, `useShellPrefs()` reads empty and drops saves, so both silently degrade to localStorage-only and never reach the user's profile. Session restore reads `session_windows` once, at mount: hand the provider prefs that are already loaded by then (an adapter whose prefs arrive later misses that load's replay, though the saved set is kept). |
967
967
  | `<ShellStringsProvider value={{ … }} locale?>` | Translates every word the shell and the kit draw on their own account — window controls, taskbar hints, dialogs, pickers, pagers, list footers, upload zones, charts, timelines. English works with NO provider; the override is a typed partial merged per section, so an incomplete catalog falls back rather than breaking. Prop-level text (`emptyText`, placeholders) always wins over the catalog. `locale` (a BCP 47 tag) writes dates and numbers that locale's way; without one nothing moves. See [Languages, dates and numbers](#languages-dates-and-numbers--shellstringsprovider) for the full catalog. |
968
+ | `<ShellLanguageProvider languages language strings? onChange? reload?>` | Which interface language is in force. Takes the tag `resolveShellLanguage` chose before mount and that language's shell catalog, mounts `ShellStringsProvider` with both, stamps `<html lang>`, and gives `useShellLanguage()` its `setLanguage`, which saves the choice and reloads. See [Choosing the language](#choosing-the-language--shelllanguageprovider). |
968
969
  | `<ShellEntityFetcherProvider value={(endpoint, id) => …}>` | How the modal stack fetches entity data. |
969
970
  | `<BugReportConfigProvider value={{ submit, list?, resolve? }}>` | Wire the bug-report flow to your backend. |
970
971
  | `<DesktopHostProvider value={{ stickyResolver?, saveShortcuts?, … }}>` | Sticky-note ref resolver + persistence callbacks. |
971
- | `<StatusBadgeProvider groups={{ status: 'success' \| ... }}>` | Status string → semantic group. |
972
+ | `<StatusBadgeProvider groups={{ status: 'success' \| ... }} labelFor?>` | Status string → semantic group. `labelFor(status)` gives every badge under it the host's words for a status (an interface in another language passes it once); a status it returns `undefined` for keeps the derived label, and a badge's own `label` still wins. `useStatusLabel()` says a status the same way outside a badge: a filter option, a search result's second line. |
972
973
  | `<PortalBrandingProvider load={...} fallback={...}>` | Loads public hostname-scoped Company Profile identity before authentication, applies its favicon and optional derived document title, and exposes it through `usePortalBranding()`. |
973
974
  | `setShellApiClient(axios)` | Module-level: register your axios instance once. |
974
975
  | `setShellAuthBridge({ user, logout })` | Module-level: register user identity / logout handler. |
@@ -995,6 +996,7 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
995
996
  | `useUndoCanEdit(canEdit)` | A form's own word on whether the record may be edited right now — `useUndoCanEdit(!isLocked)`. While false the stack records nothing, ⌘Z is left alone and the footer shows no controls. A nested `<UndoProvider canEdit={false}>` in the form's JSX says the same thing: it shadows the stack for its children AND forwards the read-only claim to the window's stack, which is where the form's own `useUndoableState` calls (made above it in the tree) register. Either form of the claim makes the whole window read-only. The positive claim, `useUndoCanEdit(true)`, is how a window that edits in place outside Edit mode — a detail with inline cells behind a permission — asks the footer for the pair. |
996
997
  | `useMultiModal()` | Manages multi-window stacking + activate/blur. |
997
998
  | `useShellAuth() / useShellPrefs() / useShellEntityFetcher() / useBugReport() / useDesktopHost()` | Context readers — the shell uses these internally; consumers may also call them. |
999
+ | `useShellLanguage()` | `{ language, languages, setLanguage }` — the active language's tag, the ones on offer, and the switch. English with nothing to switch to when no `ShellLanguageProvider` is mounted. |
998
1000
  | `useShellStrings() / useShellLocale()` | The active catalog, and the provider's `locale` (`undefined` without one) — for a host cell that should write a date or a number the way the kit does. |
999
1001
 
1000
1002
  ### Apps barrel — `efficient-shell/apps`
@@ -1475,6 +1477,35 @@ classes in `ui.css` (the timeline card's) carry pixel radii of their own.
1475
1477
  }
1476
1478
  ```
1477
1479
 
1480
+ **Corners.** A component does not pick a step of the scale for itself; it asks
1481
+ for the radius of what it is. Four role tokens, each defaulting to the step that
1482
+ role has always drawn, so they change nothing until you set one:
1483
+
1484
+ | Token | Default | Drawn by |
1485
+ |---|---|---|
1486
+ | `--radius-control` | `--radius-md` (6px) | buttons, fields, a segmented control's options |
1487
+ | `--radius-card` | `--radius-lg` (8px) | `Card`, `Dialog`, a segmented control's track |
1488
+ | `--radius-popup` | `--radius-2xl` (16px) | menus, select lists, the start menu, the search palette |
1489
+ | `--radius-window` | `--radius-2xl` (16px) | windows and their title bars |
1490
+
1491
+ They are plain custom properties: `:root { --radius-control: 2px; }` restyles
1492
+ one role and leaves the rest. Pills (switches, status badges, avatars) are round
1493
+ at every setting. Popovers, tooltips, drawers and menu rows still sit on their
1494
+ own scale steps, because each draws a different radius today and moving them
1495
+ onto a role would change how they look.
1496
+
1497
+ **Corners is also a setting of the theme.** The `corners` preference
1498
+ (`'square' | 'soft' | 'round'`, in Customization beside the colour theme) is
1499
+ stamped on `<html>` as `data-corners` by `useTheme` and
1500
+ `applyThemePrefs({ corners })`; a host with its own switcher calls
1501
+ `applyCorners(value)`. It rescales the whole radius scale, so the roles follow
1502
+ it and so does every scale utility in your own markup, and it remaps bare
1503
+ `rounded`, which the scale cannot reach. `soft` is the absence of the attribute:
1504
+ no rule applies and your own `@theme` radii still decide. `square` and `round`
1505
+ are a person's own choice, so they win over the host's theme, the way a chosen
1506
+ colour theme does. Do not write literal radii (an arbitrary-value radius class,
1507
+ an inline `borderRadius`): they are the one thing the setting cannot reach.
1508
+
1478
1509
  **Glass.** Menus, popups, listboxes and tooltips are frosted glass
1479
1510
  (`glassStyle()`). `--menu-opacity` (0–1, default `0.95`) sets how much shows
1480
1511
  through. `rosh-reduce-transparency` on `<html>` makes every popup one solid
@@ -1595,6 +1626,72 @@ the field, because what is typed there is parsed back.
1595
1626
  `formatDate(value, { locale, style })` or `fmtSliderDate(ms, locale)`, or hand it
1596
1627
  to `Intl` directly — so a host's dates and numbers read like the kit's.
1597
1628
 
1629
+ #### Choosing the language — `ShellLanguageProvider`
1630
+
1631
+ `ShellStringsProvider` can speak another language; `ShellLanguageProvider` is
1632
+ what decides which. It holds the languages you offer, the one in force and the
1633
+ reader's saved choice, stamps `<html lang>`, and mounts `ShellStringsProvider`
1634
+ for you with that language's catalog and locale.
1635
+
1636
+ A language is fixed for the life of a page. Resolve it before you mount, load
1637
+ its copy, and hand both over; `setLanguage` saves the choice and reloads. That
1638
+ is deliberate: column sets, navigation and status buckets usually read their
1639
+ copy when they are imported, a list snapshots its columns when it mounts, and
1640
+ a reload is the one switch that leaves none of them in the old language.
1641
+
1642
+ ```tsx
1643
+ import {
1644
+ ENGLISH, ShellLanguageProvider, resolveShellLanguage, useShellLanguage,
1645
+ type ShellLanguage,
1646
+ } from 'efficient-shell/ui';
1647
+
1648
+ const LANGUAGES: ShellLanguage[] = [ENGLISH, { tag: 'zh-Hans', name: '简体中文' }];
1649
+
1650
+ // Before React mounts: the saved choice, then the browser's list, then the first offered.
1651
+ const language = resolveShellLanguage({ offered: LANGUAGES });
1652
+
1653
+ <ShellLanguageProvider
1654
+ languages={LANGUAGES}
1655
+ language={language}
1656
+ strings={shellCatalogForThatLanguage} // omit for English
1657
+ onChange={tag => saveToTheAccount(tag)} // awaited, then the page reloads
1658
+ >
1659
+ <App />
1660
+ </ShellLanguageProvider>
1661
+
1662
+ const { language, languages, setLanguage } = useShellLanguage();
1663
+ ```
1664
+
1665
+ - **Resolution.** `resolveShellLanguage` reads the choice saved on the device
1666
+ (`SHELL_LANGUAGE_STORAGE_KEY`, `react-os-shell:language`), then
1667
+ `navigator.languages`, then falls back to the first language offered. A
1668
+ browser asking for `zh-CN` or `zh-SG` gets `zh-Hans`; with both Chineses on
1669
+ offer `zh-TW` gets `zh-Hant`, and with only Simplified it gets that rather
1670
+ than English. A saved choice that is no longer offered is skipped.
1671
+ `matchShellLanguage(wanted, offered)` is the matching on its own.
1672
+ - **English keeps its dates and numbers.** A language's `locale` defaults to
1673
+ its tag. `ENGLISH` carries `locale: null`, so mounting the provider for
1674
+ English moves no date and no number: the kit writes them as it does with no
1675
+ provider at all.
1676
+ - **The account.** `onChange` is where a host with accounts keeps the choice.
1677
+ It is awaited before the reload, and a failure does not stop the switch: the
1678
+ device has the choice. `saveShellLanguage(tag)` returns `true` only when the
1679
+ write reads back, for a sign-in sync that would otherwise reload on a write
1680
+ that never landed.
1681
+ - **`reload={false}`** leaves the switch to you: the choice is saved and
1682
+ `onChange` runs, and you re-render the provider with the new `language` and
1683
+ `strings`.
1684
+
1685
+ **The windowed shell's lists, records and forms are in the catalog too.**
1686
+ `EntityList`'s row menu and its copy and export toasts (`entityList`),
1687
+ `ResizableTable`'s column chooser (`columns`), a record window with no record
1688
+ to draw (`record`), `ChangePasswordForm` (`password`), `PdfActionButton`
1689
+ (`pdfAction`), `UndoControls` (`undo`), the copy icon beside a record's number
1690
+ (`copy`), the plain "Loading..." line (`loading`) and `SoundsPanel`'s scheme
1691
+ and event names (`sounds`). Their own props — `submitLabel`, `doneLabel`, the
1692
+ PDF button's `label`, a list's `errorTitle` — still win, and the keys a sound
1693
+ scheme is stored under are identifiers, never translated.
1694
+
1598
1695
  **What is not in the catalog, on purpose:** month and weekday names (the
1599
1696
  `locale` supplies them); file-format names in `BrandAssetEditor`'s default
1600
1697
  `acceptHint` and the `20ft`/`40ft` container codes (notation, not language); the
@@ -1603,7 +1700,7 @@ tables read); the sample content of the starter templates (`DashboardTemplate`,
1603
1700
  `AuthScreen` and the rest are demos to copy, not components to configure); and
1604
1701
  console warnings, which are for developers.
1605
1702
 
1606
- **The full catalog** — 39 sections, 362 keys. A function's default is shown
1703
+ **The full catalog** — 55 sections, 522 keys. A function's default is shown
1607
1704
  with sample arguments.
1608
1705
 
1609
1706
  <details><summary><code>window</code> — 17 keys</summary>
@@ -1741,7 +1838,7 @@ with sample arguments.
1741
1838
 
1742
1839
  </details>
1743
1840
 
1744
- <details><summary><code>pageTabs</code> — 13 keys</summary>
1841
+ <details><summary><code>pageTabs</code> — 16 keys</summary>
1745
1842
 
1746
1843
  | Key | Type | English default |
1747
1844
  |---|---|---|
@@ -1752,6 +1849,9 @@ with sample arguments.
1752
1849
  | `closeRight` | `string` | `Close tabs to the right` |
1753
1850
  | `closeAll` | `string` | `Close all tabs` |
1754
1851
  | `unsaved` | `string` | `Unsaved changes` |
1852
+ | `copy` | `(text: string) => string` | `Copy {text}` |
1853
+ | `copied` | `string` | `Copied` |
1854
+ | `tabMenu` | `string` | `Window menu` |
1755
1855
  | `discardTitle` | `string` | `Discard unsaved changes?` |
1756
1856
  | `discardOne` | `string` | `This page has changes that have not been saved. Close it anyway?` |
1757
1857
  | `discardMany` | `string` | `pages have changes that have not been saved. Close them anyway?` |
@@ -1845,11 +1945,12 @@ with sample arguments.
1845
1945
 
1846
1946
  </details>
1847
1947
 
1848
- <details><summary><code>list</code> — 8 keys</summary>
1948
+ <details><summary><code>list</code> — 9 keys</summary>
1849
1949
 
1850
1950
  | Key | Type | English default |
1851
1951
  |---|---|---|
1852
1952
  | `selected` | `(count: number) => string` | `3 selected` |
1953
+ | `selectedNotShown` | `(count: number, notShown: number) => string` | `3 selected, 3 not shown` |
1853
1954
  | `allLoaded` | `(total: number, label: string) => string` | `All 3 {label} loaded` |
1854
1955
  | `count` | `(total: number, label: string) => string` | `3 {label}` |
1855
1956
  | `loadedOf` | `(loaded: number, total: number, label: string) => string` | `3 of 3 {label}` |
@@ -1929,13 +2030,15 @@ with sample arguments.
1929
2030
 
1930
2031
  </details>
1931
2032
 
1932
- <details><summary><code>copy</code> — 5 keys</summary>
2033
+ <details><summary><code>copy</code> — 7 keys</summary>
1933
2034
 
1934
2035
  | Key | Type | English default |
1935
2036
  |---|---|---|
1936
2037
  | `copy` | `string` | `Copy` |
1937
2038
  | `copyLabel` | `(label: string) => string` | `Copy {label}` |
1938
2039
  | `copied` | `string` | `Copied` |
2040
+ | `copyValue` | `(value: string) => string` | `Copy {value}` |
2041
+ | `copiedValue` | `string` | `Copied!` |
1939
2042
  | `copiedLabel` | `(label: string) => string` | `{label} copied` |
1940
2043
  | `failed` | `string` | `Could not copy to the clipboard` |
1941
2044
 
@@ -2052,6 +2155,60 @@ with sample arguments.
2052
2155
 
2053
2156
  </details>
2054
2157
 
2158
+ <details><summary><code>resizeHandle</code> — 2 keys</summary>
2159
+
2160
+ | Key | Type | English default |
2161
+ |---|---|---|
2162
+ | `label` | `string` | `Resize panel` |
2163
+ | `hint` | `string` | `Drag to resize · double-click to reset` |
2164
+
2165
+ </details>
2166
+
2167
+ <details><summary><code>number</code> — 2 keys</summary>
2168
+
2169
+ | Key | Type | English default |
2170
+ |---|---|---|
2171
+ | `increase` | `string` | `Increase` |
2172
+ | `decrease` | `string` | `Decrease` |
2173
+
2174
+ </details>
2175
+
2176
+ <details><summary><code>slider</code> — 1 keys</summary>
2177
+
2178
+ | Key | Type | English default |
2179
+ |---|---|---|
2180
+ | `value` | `(label: string) => string` | `{label} value` |
2181
+
2182
+ </details>
2183
+
2184
+ <details><summary><code>color</code> — 9 keys</summary>
2185
+
2186
+ | Key | Type | English default |
2187
+ |---|---|---|
2188
+ | `swatch` | `(label: string, value: string) => string` | `{label}: {value}` |
2189
+ | `none` | `string` | `None` |
2190
+ | `panel` | `(label: string) => string` | `{label} picker` |
2191
+ | `square` | `string` | `Saturation and brightness` |
2192
+ | `squareValue` | `(saturation: number, brightness: number) => string` | `Saturation 3%, brightness 3%` |
2193
+ | `hue` | `string` | `Hue` |
2194
+ | `hex` | `(label: string) => string` | `{label} hex code` |
2195
+ | `presets` | `string` | `Presets` |
2196
+ | `clear` | `string` | `No color` |
2197
+
2198
+ </details>
2199
+
2200
+ <details><summary><code>tree</code> — 5 keys</summary>
2201
+
2202
+ | Key | Type | English default |
2203
+ |---|---|---|
2204
+ | `empty` | `string` | `Nothing here yet` |
2205
+ | `dropHere` | `string` | `Drop here` |
2206
+ | `dragHint` | `string` | `Drag to move` |
2207
+ | `moveHint` | `string` | `Alt+Arrow Up or Alt+Arrow Down moves it` |
2208
+ | `insertHere` | `string` | `Insert here` |
2209
+
2210
+ </details>
2211
+
2055
2212
  <details><summary><code>layout</code> — 17 keys</summary>
2056
2213
 
2057
2214
  | Key | Type | English default |
@@ -2276,6 +2433,183 @@ with sample arguments.
2276
2433
 
2277
2434
  </details>
2278
2435
 
2436
+ <details><summary><code>map</code> — 14 keys</summary>
2437
+
2438
+ | Key | Type | English default |
2439
+ |---|---|---|
2440
+ | `failed` | `string` | `The map could not be drawn here. The list has everything it shows.` |
2441
+ | `approximate` | `(label: string) => string` | `{label}, approximate location` |
2442
+ | `approximateTitle` | `(label: string) => string` | `{label} (approximate)` |
2443
+ | `places` | `(count: number) => string` | `places` (`place` for one) |
2444
+ | `group` | `(count: number, noun: string) => string` | `3 {noun} here` |
2445
+ | `stops` | `(numbers: string) => string` | `Stops {numbers}` |
2446
+ | `closeBy` | `(count: number, noun: string) => string` | `3 more {noun} close by, drawn when the map is closer` |
2447
+ | `spread` | `string` | `Press to see them apart` |
2448
+ | `cycle` | `string` | `They stand on one spot: press for the next of them` |
2449
+ | `fullScreen` | `string` | `Full screen` |
2450
+ | `leaveFullScreen` | `string` | `Leave full screen` |
2451
+ | `chosen` | `(label: string) => string` | `{label}, chosen on the map` |
2452
+ | `closeCard` | `string` | `Close` |
2453
+ | `openRecord` | `string` | `Open` |
2454
+
2455
+ </details>
2456
+
2457
+ <details><summary><code>pdf</code> — 16 keys</summary>
2458
+
2459
+ | Key | Type | English default |
2460
+ |---|---|---|
2461
+ | `previousPage` | `string` | `Previous page` |
2462
+ | `nextPage` | `string` | `Next page` |
2463
+ | `pageOf` | `(page: number, total: number) => string` | `2 / 5` |
2464
+ | `zoomOut` | `string` | `Zoom out` |
2465
+ | `zoomIn` | `string` | `Zoom in` |
2466
+ | `zoom` | `string` | `Zoom` |
2467
+ | `fitWidth` | `string` | `Fit width` |
2468
+ | `fitPage` | `string` | `Fit page` |
2469
+ | `fit` | `string` | `Fit` |
2470
+ | `fitHint` | `string` | `Fit to the pane: follows its size until you zoom by hand` |
2471
+ | `print` | `string` | `Print` |
2472
+ | `download` | `string` | `Download` |
2473
+ | `email` | `string` | `Email` |
2474
+ | `loading` | `string` | `Loading PDF…` |
2475
+ | `loadFailed` | `string` | `Failed to load PDF` |
2476
+ | `allowPopups` | `string` | `Allow popups to print` |
2477
+
2478
+ </details>
2479
+
2480
+ <details><summary><code>loading</code> — 1 keys</summary>
2481
+
2482
+ | Key | Type | English default |
2483
+ |---|---|---|
2484
+ | `label` | `string` | `Loading...` |
2485
+
2486
+ </details>
2487
+
2488
+ <details><summary><code>entityList</code> — 15 keys</summary>
2489
+
2490
+ | Key | Type | English default |
2491
+ |---|---|---|
2492
+ | `open` | `string` | `Open` |
2493
+ | `copy` | `string` | `Copy` |
2494
+ | `copyColumn` | `(column: string) => string` | `Copy {column}` |
2495
+ | `copyRows` | `(count: number) => string` | `Copy 3 rows` (`Copy row` for one) |
2496
+ | `exportSelected` | `string` | `Export selected to CSV` |
2497
+ | `selectAll` | `string` | `Select all` |
2498
+ | `selectAllLoaded` | `(loaded: number) => string` | `Select all 3 loaded` |
2499
+ | `clearSelection` | `string` | `Clear selection` |
2500
+ | `refresh` | `string` | `Refresh` |
2501
+ | `copiedText` | `string` | `Text` |
2502
+ | `copiedRows` | `(count: number) => string` | `3 rows` (`Row` for one) |
2503
+ | `exported` | `(count: number) => string` | `Exported 3 rows.` (`Exported 1 row.` for one) |
2504
+ | `exportTruncated` | `(kept: number, asked: number) => string` | `Exported 5000 of 8000 rows. The server capped this export, so the file is incomplete.` |
2505
+ | `exportTruncatedUnknown` | `(asked: number) => string` | `Exported only some of the 8000 rows. The server capped this export, so the file is incomplete.` |
2506
+ | `exportFailed` | `string` | `CSV export failed.` |
2507
+
2508
+ </details>
2509
+
2510
+ <details><summary><code>columns</code> — 9 keys</summary>
2511
+
2512
+ | Key | Type | English default |
2513
+ |---|---|---|
2514
+ | `choose` | `string` | `Choose columns` |
2515
+ | `heading` | `string` | `Columns` |
2516
+ | `reset` | `string` | `Reset` |
2517
+ | `saveDefault` | `string` | `Save as default for all users` |
2518
+ | `savedDefault` | `string` | `Saved as default` |
2519
+ | `select` | `string` | `Select` |
2520
+ | `selectDone` | `string` | `Done` |
2521
+ | `showSelection` | `string` | `Show selection column` |
2522
+ | `hideSelection` | `string` | `Hide selection column` |
2523
+
2524
+ </details>
2525
+
2526
+ <details><summary><code>record</code> — 8 keys</summary>
2527
+
2528
+ | Key | Type | English default |
2529
+ |---|---|---|
2530
+ | `loading` | `string` | `Loading record…` |
2531
+ | `signInRequired` | `string` | `Sign in required` |
2532
+ | `signInBody` | `string` | `Your session may have expired. Sign in again to open this record.` |
2533
+ | `notFound` | `string` | `Record not found` |
2534
+ | `loadFailed` | `string` | `Couldn't load record` |
2535
+ | `checkConnection` | `string` | `Check your connection and try again.` |
2536
+ | `rejected` | `string` | `The server rejected this request.` |
2537
+ | `retry` | `string` | `Try again` |
2538
+
2539
+ </details>
2540
+
2541
+ <details><summary><code>password</code> — 11 keys</summary>
2542
+
2543
+ | Key | Type | English default |
2544
+ |---|---|---|
2545
+ | `current` | `string` | `Current Password` |
2546
+ | `newPassword` | `string` | `New Password` |
2547
+ | `confirm` | `string` | `Confirm Password` |
2548
+ | `submit` | `string` | `Change Password` |
2549
+ | `submitting` | `string` | `Changing...` |
2550
+ | `done` | `string` | `Done` |
2551
+ | `currentRequired` | `string` | `Current password is required.` |
2552
+ | `tooShort` | `(minLength: number) => string` | `New password must be at least 8 characters.` |
2553
+ | `mismatch` | `string` | `Passwords do not match.` |
2554
+ | `failed` | `string` | `Failed to change password.` |
2555
+ | `changed` | `string` | `Your password has been changed successfully.` |
2556
+
2557
+ </details>
2558
+
2559
+ <details><summary><code>pdfAction</code> — 10 keys</summary>
2560
+
2561
+ | Key | Type | English default |
2562
+ |---|---|---|
2563
+ | `label` | `string` | `PDF` |
2564
+ | `loading` | `string` | `Loading...` |
2565
+ | `preview` | `string` | `Preview` |
2566
+ | `download` | `string` | `Download` |
2567
+ | `email` | `string` | `Send by Email` |
2568
+ | `downloaded` | `(filename: string) => string` | `{filename} downloaded.` |
2569
+ | `tookTooLong` | `(filename: string) => string` | `{filename} took too long to load. Please try again.` |
2570
+ | `previewLoading` | `string` | `LOADING PDF` |
2571
+ | `previewTookTooLong` | `string` | `This document took too long to load. Close this window and try again.` |
2572
+ | `previewFailed` | `string` | `Failed to load PDF.` |
2573
+
2574
+ </details>
2575
+
2576
+ <details><summary><code>undo</code> — 6 keys</summary>
2577
+
2578
+ | Key | Type | English default |
2579
+ |---|---|---|
2580
+ | `undo` | `string` | `Undo` |
2581
+ | `redo` | `string` | `Redo` |
2582
+ | `undoStep` | `(label: string, keys: string) => string` | `Undo {label} ({keys})` |
2583
+ | `redoStep` | `(label: string, keys: string) => string` | `Redo {label} ({keys})` |
2584
+ | `nothingToUndo` | `string` | `Nothing to undo` |
2585
+ | `nothingToRedo` | `string` | `Nothing to redo` |
2586
+
2587
+ </details>
2588
+
2589
+ <details><summary><code>sounds</code> — 17 keys</summary>
2590
+
2591
+ | Key | Type | English default |
2592
+ |---|---|---|
2593
+ | `title` | `string` | `Sounds` |
2594
+ | `enable` | `string` | `Sound effects` |
2595
+ | `autosave` | `string` | `Changes are saved automatically` |
2596
+ | `choice` | `(event: string, pack: string) => string` | `{event} — {pack}` |
2597
+ | `packClassic` | `string` | `Classic` |
2598
+ | `packMinimal` | `string` | `Minimal` |
2599
+ | `packRetro` | `string` | `Retro` |
2600
+ | `packSoft` | `string` | `Soft` |
2601
+ | `packArcade` | `string` | `Arcade` |
2602
+ | `packSilent` | `string` | `Silent` |
2603
+ | `eventClick` | `string` | `Click` |
2604
+ | `eventSuccess` | `string` | `Success` |
2605
+ | `eventError` | `string` | `Error` |
2606
+ | `eventNotification` | `string` | `Notification` |
2607
+ | `eventStartup` | `string` | `Startup` |
2608
+ | `eventTimerDone` | `string` | `Timer` |
2609
+ | `eventLogout` | `string` | `Logout` |
2610
+
2611
+ </details>
2612
+
2279
2613
  ### Brand surfaces — `efficient-shell/brand.css`
2280
2614
 
2281
2615
  For output that is not a portal window — a report, a published artifact, a
@@ -2553,8 +2887,8 @@ missing or cannot be read. All of them are also exported from
2553
2887
  | `formatTime(value, options?)` | `9:30 am` — twelve-hour, small am/pm; a `locale` gives the locale's own clock. |
2554
2888
  | `getUserTimezone()`, `setUserTimezone(zone)`, `syncTimezoneFromProfile(zone)`, `TZ_AUTO` | The zone setting. `undefined` / `'auto'` follow the browser. Call `syncTimezoneFromProfile` when the signed-in user loads; a blank value changes nothing. |
2555
2889
  | `getUserDateFormat()`, `setUserDateFormat(key)`, `DATE_FORMAT_OPTIONS` | The format setting and the 16 formats a settings page offers. |
2556
- | `timeAgo(value, { precise? })` | `just now` under a minute, then `5m ago`, `3h ago`, `2d ago`; from seven days on the date (`formatDate`). A future time is `just now`. `precise` counts seconds (`12s ago`) for a status screen. Takes a string, a `Date` or epoch ms. |
2557
- | `<TimeAgo value precise? className? />` | `timeAgo` in a `<time>` whose hover (`title`) is the full `formatDateTime`. Use it wherever a relative time is shown. |
2890
+ | `timeAgo(value, { precise? })` | `just now` under a minute, then `5m ago`, `3h ago`, `2d ago`; from seven days on the date (`formatDate`). A future time is `just now`. `precise` counts seconds (`12s ago`) for a status screen. Takes a string, a `Date` or epoch ms. With `locale` (a BCP 47 tag) the words are `Intl.RelativeTimeFormat`'s in that language, and the fallback date is written in it too. |
2891
+ | `<TimeAgo value precise? locale? className? />` | `timeAgo` in a `<time>` whose hover (`title`) is the full `formatDateTime`. Use it wherever a relative time is shown. It follows `ShellStringsProvider`'s `locale`; pass `locale` to override. |
2558
2892
  | `setCurrencyFormats(catalog)` | Hand the kit the currency catalog (`/api/currencies/`: `{ AUD: { symbol: 'A$', decimals: 2 }, … }`) once a session starts. Until then amounts show their code. |
2559
2893
  | `formatMoney(amount, currency?, { whole? })` | `A$1,234.50`, `-€50.00`, `CHF 1,200.00`. The catalog's symbol — an unknown code prints as the code, never a guessed `$`. Minus first, commas, the catalog's decimals (2 by default); `whole` drops them (`A$3,841`) for a storefront card. No currency prints the bare figure. Takes a number or the API's string. |
2560
2894
  | `currencySymbol(code)`, `currencyDecimals(code)` | The two catalog lookups `formatMoney` uses. |
@@ -2581,6 +2915,68 @@ missing or cannot be read. All of them are also exported from
2581
2915
  | `dismissPopups()`, `OVERLAY_OPEN_EVENT` | An overlay calls `dismissPopups()` as it opens; every open kit popup (`Select`, `SearchableSelect`, `TagInput`, `DatePicker`, `DateRangePicker`, `PopupMenu` via its `onClose`, `ServerStatusIndicator`) closes rather than floating over the overlay. The kit's own overlays already call it. A consumer's own popup listens for `OVERLAY_OPEN_EVENT` on `window` while open. |
2582
2916
  | `Kbd` constants — `MOD`, `ALT`, `SHIFT`, `ENTER`, `ALT_SHIFT_E`, `CMD_K`, … | Symbol constants for rendering keyboard shortcuts. |
2583
2917
 
2918
+ ### Icons — `efficient-shell/icons`
2919
+
2920
+ Every icon comes through this package (harness UI-24). A component asks for an
2921
+ icon by what it **means**, and the set the theme chose draws it:
2922
+
2923
+ ```tsx
2924
+ import { Icon, registerIconSet } from 'efficient-shell/icons';
2925
+ import { heroicons } from 'efficient-shell/icons/heroicons';
2926
+
2927
+ registerIconSet(heroicons); // once, at startup, in client code
2928
+
2929
+ <Icon name="trash" /> // 1em square, the text's colour
2930
+ <Icon name="search" size={20} tone="muted" />
2931
+ <Icon name="warning" title="Overdue" /> // named: an image, not decoration
2932
+ ```
2933
+
2934
+ `Icon` and the registry are also exported from `efficient-shell/ui` and the
2935
+ root entry. They import React and nothing else.
2936
+
2937
+ **The catalogue.** `IconName` is 197 meanings (`add`, `close`, `edit`, `trash`,
2938
+ `search`, `download`, `chevron-down`, `warning`, …), built from the icons the
2939
+ EFFICIENT frontends import today. `<Icon name>` is typed to it, so a misspelt
2940
+ meaning does not compile. To add one, add it to `src/icons/catalogue.json` with
2941
+ its icon in every package and run `npm run icons`.
2942
+
2943
+ **The sets.** Each is its own subpath, so a host bundles only what it
2944
+ registers, and the icon package behind it is an optional peer:
2945
+
2946
+ | Import | Set | Draws | Needs |
2947
+ |---|---|---|---|
2948
+ | `efficient-shell/icons/heroicons` | `heroicons` | the whole catalogue | `@heroicons/react` |
2949
+ | `efficient-shell/icons/lucide` | `lucide` | the whole catalogue | `lucide-react` (0.577 or later) |
2950
+ | `efficient-shell/icons/efficient` | `efficient` | the 165 house glyphs, which draw 58 of the catalogue meanings | nothing |
2951
+
2952
+ The first set registered is the default (`setDefaultIconSet(name)` changes
2953
+ that). A meaning the chosen set does not draw falls back to the default set, so
2954
+ a partial set never leaves a hole.
2955
+
2956
+ **Part of the theme.** The `icon_set` preference sits beside the colour theme
2957
+ and the corners: `useTheme` and `applyThemePrefs({ icon_set })` choose the set
2958
+ and stamp `data-icon-set` on `<html>`, and every `<Icon>` on the page redraws.
2959
+ A host with its own switcher calls `applyIconSet(name)` or `setIconSet(name)`;
2960
+ `iconSets()` lists what is registered. Like `corners`, a call without the key
2961
+ leaves the choice alone.
2962
+
2963
+ **A product's own glyphs.** An editor's block types and settings rows are not
2964
+ in the catalogue. Register them and draw them by `glyph`, with no icon package
2965
+ imported:
2966
+
2967
+ ```tsx
2968
+ registerIcons('editor', { 'block-product': BlockProductGlyph });
2969
+ <Icon glyph="block-product" />
2970
+ ```
2971
+
2972
+ They are found under any theme. `efficient-shell/icons/efficient` also exports
2973
+ each house glyph as a component (`TrashIcon`, `StylesIcon`, `PaddingTopIcon`,
2974
+ …, `size` 18 by default) for code that wants one drawing whatever the theme.
2975
+
2976
+ With no set registered, `<Icon>` draws nothing and warns once per meaning. The
2977
+ kit's own components (a window's close button, a filter chip) keep drawing
2978
+ through a built-in fallback, so a host that registers nothing sees no change.
2979
+
2584
2980
  ### The UI kit check — `efficient-shell-ui-check`
2585
2981
 
2586
2982
  Every surface a person operates takes its UI from this package (harness
@@ -2616,9 +3012,10 @@ itself where the kit has an answer:
2616
3012
  | `native` | `window.confirm` / `alert` / `prompt`, or a bare call nothing in the file binds | `confirm`, `confirmDestructive`, `prompt`, `toast` |
2617
3013
  | `dark` | a `dark:` variant in a class string | the light class alone; the kit remaps it |
2618
3014
  | `hex` | a hex colour in a `.tsx` string (not in `href`, `to`, `id`, `key`, `htmlFor`) | a token or a palette class |
3015
+ | `radius` | a literal corner radius: an arbitrary-value radius class (`rounded-[4px]`) or an inline `borderRadius` number or length (not `inherit`, `var(…)`, `50%` or `9999`) | a scale utility or a role token; the theme's Corners setting shapes them |
2619
3016
  | `kit` | an import of a second UI kit (`@headlessui`, `@radix-ui`, `@mui`, `antd`, `sonner`, …) | the kit's component |
2620
- | `icon` | an import of an icon package (`@heroicons`, `lucide-react`, `@tabler/icons`, …) | the icon through this package |
2621
- | `kit-dep` | one of those packages declared in `package.json` (not `devDependencies`) | remove it |
3017
+ | `icon` | an import of an icon package (`@heroicons`, `lucide-react`, `@tabler/icons`, …) | `<Icon name>` from `efficient-shell/icons` |
3018
+ | `kit-dep` | one of those packages declared in `package.json` (not `devDependencies`, and not one the shell needs from an entry the repository imports: `@headlessui/react` and `@heroicons/react` for the root entry, an icon set's own package) | remove it |
2622
3019
 
2623
3020
  Known gaps: `rgb()` / `hsl()` colours, colours in `.ts` maps, and
2624
3021
  `createElement('button')` are not counted.
@@ -1,29 +1,29 @@
1
- import{peekBrowserStartUrl as e,claimBrowserStartUrl as t}from"./chunk-LWLCNQY7.js";import{AboutApp as o}from"./chunk-6MBERZOS.js";import"./chunk-7U3F53IG.js";import{WindowTitle as r}from"./chunk-LW27IM3S.js";import"./chunk-A7Q4BCPI.js";import"./chunk-DSSLOQDW.js";import"./chunk-5A7MWTXX.js";import"./chunk-JYLI2WXW.js";import"./chunk-6KO2U66R.js";import{confirm as n}from"./chunk-M4DB7UBZ.js";import"./chunk-N3ZXQE4M.js";import"./chunk-AFYQTVZO.js";import"./chunk-HZZNJHRH.js";import"./chunk-X4FJ54PP.js";import"./chunk-NCQLGQYR.js";import"./chunk-D65T4H6J.js";import{useRef as a,useEffect as i,useState as s}from"react";import{jsxs as l,jsx as c,Fragment as m}from"react/jsx-runtime";var d="react-os-shell:browser-bookmarks",u="react-os-shell:browser-homepage",h="https://en.wikipedia.org/wiki/Main_Page",p=[{label:"Wikipedia",url:"https://en.wikipedia.org/wiki/Main_Page"},{label:"DevDocs",url:"https://devdocs.io"},{label:"Example",url:"https://example.com"}],g=new Set(["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","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","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","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","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","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","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","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","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","ua ug uk us uy uz va vc ve vg vi vn vu wf ws ye yt za zm zw","com net org edu gov mil int info biz name pro app dev xyz online site tech","store shop cloud design digital agency studio media news blog live life","world today email systems solutions services software page link space host","press wiki team group works careers finance capital energy academy"].join(" ").split(" "));function b(e){let t=e.trim();return t?/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:function(e){const t=/^([\w-]+\.)+([a-z]{2,})(\/.*)?$/i.exec(e);return null!==t&&g.has(t[2].toLowerCase())}(t)?"https://"+t:"https://duckduckgo.com/?q="+encodeURIComponent(t):""}var k=["duckduckgo.com","developer.mozilla.org","google.com","gmail.com","youtube.com","facebook.com","instagram.com","whatsapp.com","twitter.com","x.com","github.com","gitlab.com","linkedin.com","reddit.com","pinterest.com","amazon.com","amazon.ca","amazon.co.uk","apple.com","icloud.com","microsoft.com","outlook.com","live.com","office.com","netflix.com","spotify.com","paypal.com","stripe.com","chat.openai.com","chatgpt.com","claude.ai"];function f(){if("undefined"==typeof window)return p;try{const e=localStorage.getItem(d);if(!e)return p;const t=JSON.parse(e);if(Array.isArray(t)&&t.every(e=>"string"==typeof e?.url))return t}catch{}return p}function x(){return"undefined"==typeof window?h:localStorage.getItem(u)||h}function w(){const m=a(void 0);void 0===m.current&&(m.current=e()),i(()=>{null!=m.current&&t(m.current)},[]);const h=m.current?b(m.current.url):"",[p,g]=s(x),[k,w]=s(h||p),[N,L]=s(k),[j,C]=s([k]),[z,M]=s(0),[W,S]=s(0),[R,B]=s(f),[H,A]=s(!1),[E,D]=s(null),I=a(null),O=a(null);i(()=>{try{localStorage.setItem(d,JSON.stringify(R))}catch{}},[R]);const T=e=>{const t=b(e);t&&(w(t),L(t),C(e=>{const o=e.slice(0,z+1);return o.push(t),o}),M(e=>e+1),A(!1))},U=()=>{"undefined"!=typeof window&&window.open(k,"_blank","noopener,noreferrer")},P=()=>{const e=E?.trim();e?(B(t=>[...t,{label:e,url:k}]),D(null)):D(null)};i(()=>{if(null===E)return;I.current?.focus(),I.current?.select();const e=e=>{const t=e.target;t?.closest("[data-bookmark-popover]")||D(null)},t=setTimeout(()=>document.addEventListener("mousedown",e),0);return()=>{clearTimeout(t),document.removeEventListener("mousedown",e)}},[null!==E]);const F=e=>{B(t=>t.filter((t,o)=>o!==e))},Q=z>0,X=z<j.length-1,q=R.some(e=>e.url===k),J="p-1.5 rounded hover:bg-gray-200 transition-colors text-gray-600 disabled:opacity-30";/* @__PURE__ */
1
+ import{peekBrowserStartUrl as e,claimBrowserStartUrl as t}from"./chunk-LWLCNQY7.js";import{AboutApp as o}from"./chunk-XHWT4DH2.js";import{WindowTitle as r}from"./chunk-X5LH3UOJ.js";import"./chunk-DSSLOQDW.js";import"./chunk-6TDPY7WG.js";import"./chunk-XJX3NA37.js";import"./chunk-CQN5CFKA.js";import"./chunk-5A7MWTXX.js";import"./chunk-EXXHI5DO.js";import"./chunk-PGVUO7GB.js";import"./chunk-HZZNJHRH.js";import"./chunk-D65T4H6J.js";import{confirm as n}from"./chunk-FIGVDYJR.js";import"./chunk-AFYQTVZO.js";import"./chunk-X4FJ54PP.js";import"./chunk-S5QF4VFG.js";import"./chunk-YKZHOXI4.js";import{useRef as a,useEffect as i,useState as s}from"react";import{jsxs as l,jsx as c,Fragment as m}from"react/jsx-runtime";var d="react-os-shell:browser-bookmarks",u="react-os-shell:browser-homepage",h="https://en.wikipedia.org/wiki/Main_Page",p=[{label:"Wikipedia",url:"https://en.wikipedia.org/wiki/Main_Page"},{label:"DevDocs",url:"https://devdocs.io"},{label:"Example",url:"https://example.com"}],g=new Set(["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","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","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","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","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","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","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","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","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","ua ug uk us uy uz va vc ve vg vi vn vu wf ws ye yt za zm zw","com net org edu gov mil int info biz name pro app dev xyz online site tech","store shop cloud design digital agency studio media news blog live life","world today email systems solutions services software page link space host","press wiki team group works careers finance capital energy academy"].join(" ").split(" "));function b(e){let t=e.trim();return t?/^[a-z][a-z0-9+.-]*:\/\//i.test(t)?t:function(e){const t=/^([\w-]+\.)+([a-z]{2,})(\/.*)?$/i.exec(e);return null!==t&&g.has(t[2].toLowerCase())}(t)?"https://"+t:"https://duckduckgo.com/?q="+encodeURIComponent(t):""}var k=["duckduckgo.com","developer.mozilla.org","google.com","gmail.com","youtube.com","facebook.com","instagram.com","whatsapp.com","twitter.com","x.com","github.com","gitlab.com","linkedin.com","reddit.com","pinterest.com","amazon.com","amazon.ca","amazon.co.uk","apple.com","icloud.com","microsoft.com","outlook.com","live.com","office.com","netflix.com","spotify.com","paypal.com","stripe.com","chat.openai.com","chatgpt.com","claude.ai"];function f(){if("undefined"==typeof window)return p;try{const e=localStorage.getItem(d);if(!e)return p;const t=JSON.parse(e);if(Array.isArray(t)&&t.every(e=>"string"==typeof e?.url))return t}catch{}return p}function x(){return"undefined"==typeof window?h:localStorage.getItem(u)||h}function w(){const m=a(void 0);void 0===m.current&&(m.current=e()),i(()=>{null!=m.current&&t(m.current)},[]);const h=m.current?b(m.current.url):"",[p,g]=s(x),[k,w]=s(h||p),[N,L]=s(k),[j,C]=s([k]),[z,H]=s(0),[M,W]=s(0),[S,R]=s(f),[A,D]=s(!1),[B,O]=s(null),X=a(null),E=a(null);i(()=>{try{localStorage.setItem(d,JSON.stringify(S))}catch{}},[S]);const T=e=>{const t=b(e);t&&(w(t),L(t),C(e=>{const o=e.slice(0,z+1);return o.push(t),o}),H(e=>e+1),D(!1))},F=()=>{"undefined"!=typeof window&&window.open(k,"_blank","noopener,noreferrer")},I=()=>{const e=B?.trim();e?(R(t=>[...t,{label:e,url:k}]),O(null)):O(null)};i(()=>{if(null===B)return;X.current?.focus(),X.current?.select();const e=e=>{const t=e.target;t?.closest("[data-bookmark-popover]")||O(null)},t=setTimeout(()=>document.addEventListener("mousedown",e),0);return()=>{clearTimeout(t),document.removeEventListener("mousedown",e)}},[null!==B]);const P=e=>{R(t=>t.filter((t,o)=>o!==e))},V=z>0,G=z<j.length-1,J=S.some(e=>e.url===k),U="p-1.5 rounded hover:bg-gray-200 transition-colors text-gray-600 disabled:opacity-30";/* @__PURE__ */
2
2
  return l("div",{className:"relative flex flex-col h-full bg-white",children:[
3
3
  /* @__PURE__ */c(r,{title:`Browser - ${v(k)}`}),
4
4
  /* @__PURE__ */c(o,{app:"browser"}),
5
5
  /* @__PURE__ */l("div",{className:"flex items-center gap-1 px-2 py-1.5 border-b border-gray-200 bg-gray-50 shrink-0",children:[
6
- /* @__PURE__ */c("button",{onClick:()=>{if(z>0){const e=z-1;M(e),w(j[e]),L(j[e]),A(!1)}},disabled:!Q,className:J,title:"Back",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.75 19.5L8.25 12l7.5-7.5"})})}),
7
- /* @__PURE__ */c("button",{onClick:()=>{if(z<j.length-1){const e=z+1;M(e),w(j[e]),L(j[e]),A(!1)}},disabled:!X,className:J,title:"Forward",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8.25 4.5l7.5 7.5-7.5 7.5"})})}),
8
- /* @__PURE__ */c("button",{onClick:()=>S(e=>e+1),className:J,title:"Refresh",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */c("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"})})}),
9
- /* @__PURE__ */c("button",{onClick:()=>T(p),className:J,title:"Home",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */c("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"})})}),
6
+ /* @__PURE__ */c("button",{onClick:()=>{if(z>0){const e=z-1;H(e),w(j[e]),L(j[e]),D(!1)}},disabled:!V,className:U,title:"Back",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M15.75 19.5L8.25 12l7.5-7.5"})})}),
7
+ /* @__PURE__ */c("button",{onClick:()=>{if(z<j.length-1){const e=z+1;H(e),w(j[e]),L(j[e]),D(!1)}},disabled:!G,className:U,title:"Forward",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M8.25 4.5l7.5 7.5-7.5 7.5"})})}),
8
+ /* @__PURE__ */c("button",{onClick:()=>W(e=>e+1),className:U,title:"Refresh",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */c("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"})})}),
9
+ /* @__PURE__ */c("button",{onClick:()=>T(p),className:U,title:"Home",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */c("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"})})}),
10
10
  /* @__PURE__ */c("form",{onSubmit:e=>{e.preventDefault(),T(N)},className:"flex-1 flex items-center mx-1",children:/* @__PURE__ */l("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",children:[
11
11
  /* @__PURE__ */c("span",{className:"px-2 text-gray-400 text-xs",children:k.startsWith("https://")?"🔒":"⚠️"}),
12
12
  /* @__PURE__ */c("input",{type:"text",value:N,onChange:e=>L(e.target.value),onFocus:e=>e.target.select(),className:"flex-1 px-1 py-1 text-sm bg-transparent outline-none font-mono",spellCheck:!1,placeholder:"Enter URL or search…"}),k!==N&&/* @__PURE__ */c("button",{type:"button",onClick:()=>L(k),className:"px-1.5 text-[10px] text-gray-400 hover:text-gray-700",title:"Reset to current URL",children:"×"})]})}),
13
- /* @__PURE__ */c("button",{"data-bookmark-popover":!0,onClick:()=>q?F(R.findIndex(e=>e.url===k)):void D(v(k)),className:J+" "+(q?"text-yellow-500":""),title:q?"Remove bookmark":"Add bookmark",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:q?"currentColor":"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */c("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"})})}),
14
- /* @__PURE__ */c("button",{onClick:U,className:J,title:"Open in new tab",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */c("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"})})}),
15
- /* @__PURE__ */c("button",{onClick:()=>A(e=>!e),className:J,title:"Embedding help",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */c("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"})})})]}),null!==E&&/* @__PURE__ */l("div",{"data-bookmark-popover":!0,className:"absolute right-3 top-12 z-30 w-72 bg-white border border-gray-200 rounded-md shadow-lg p-3",children:[
13
+ /* @__PURE__ */c("button",{"data-bookmark-popover":!0,onClick:()=>J?P(S.findIndex(e=>e.url===k)):void O(v(k)),className:U+" "+(J?"text-yellow-500":""),title:J?"Remove bookmark":"Add bookmark",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:J?"currentColor":"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */c("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"})})}),
14
+ /* @__PURE__ */c("button",{onClick:F,className:U,title:"Open in new tab",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */c("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"})})}),
15
+ /* @__PURE__ */c("button",{onClick:()=>D(e=>!e),className:U,title:"Embedding help",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */c("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"})})})]}),null!==B&&/* @__PURE__ */l("div",{"data-bookmark-popover":!0,className:"absolute right-3 top-12 z-30 w-72 bg-white border border-gray-200 rounded-md shadow-lg p-3",children:[
16
16
  /* @__PURE__ */c("div",{className:"text-[11px] font-semibold uppercase tracking-wide text-gray-500 mb-1",children:"Bookmark this page"}),
17
17
  /* @__PURE__ */c("div",{className:"text-[11px] text-gray-400 truncate mb-2 font-mono",title:k,children:k}),
18
- /* @__PURE__ */c("input",{ref:I,type:"text",value:E,onChange:e=>D(e.target.value),onKeyDown:e=>{"Enter"===e.key?P():"Escape"===e.key&&D(null)},placeholder:"Name",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"}),
18
+ /* @__PURE__ */c("input",{ref:X,type:"text",value:B,onChange:e=>O(e.target.value),onKeyDown:e=>{"Enter"===e.key?I():"Escape"===e.key&&O(null)},placeholder:"Name",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"}),
19
19
  /* @__PURE__ */l("div",{className:"flex justify-end gap-1 mt-2",children:[
20
- /* @__PURE__ */c("button",{onClick:()=>D(null),className:"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded",children:"Cancel"}),
21
- /* @__PURE__ */c("button",{onClick:P,disabled:!E.trim(),className:"px-2 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-40",children:"Save"})]})]}),
22
- /* @__PURE__ */l("div",{className:"flex items-center gap-0.5 px-2 py-1 border-b border-gray-200 bg-white shrink-0 overflow-x-auto",children:[0===R.length&&/* @__PURE__ */c("span",{className:"text-[11px] text-gray-400 italic px-2",children:"No bookmarks yet — star the address bar to add one."}),R.map((e,t)=>/* @__PURE__ */l("button",{onClick:()=>T(e.url),onContextMenu:async o=>{o.preventDefault();await n({title:"Remove bookmark",message:`"${e.label}" will be removed from your bookmarks.`,confirmLabel:"Remove",variant:"danger"})&&F(t)},className:"flex items-center gap-1.5 px-2 py-0.5 rounded text-[12px] text-gray-700 hover:bg-gray-100 whitespace-nowrap",title:`${e.url}\n(right-click to remove)`,children:[
20
+ /* @__PURE__ */c("button",{onClick:()=>O(null),className:"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded",children:"Cancel"}),
21
+ /* @__PURE__ */c("button",{onClick:I,disabled:!B.trim(),className:"px-2 py-1 text-xs bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-40",children:"Save"})]})]}),
22
+ /* @__PURE__ */l("div",{className:"flex items-center gap-0.5 px-2 py-1 border-b border-gray-200 bg-white shrink-0 overflow-x-auto",children:[0===S.length&&/* @__PURE__ */c("span",{className:"text-[11px] text-gray-400 italic px-2",children:"No bookmarks yet — star the address bar to add one."}),S.map((e,t)=>/* @__PURE__ */l("button",{onClick:()=>T(e.url),onContextMenu:async o=>{o.preventDefault();await n({title:"Remove bookmark",message:`"${e.label}" will be removed from your bookmarks.`,confirmLabel:"Remove",variant:"danger"})&&P(t)},className:"flex items-center gap-1.5 px-2 py-0.5 rounded text-[12px] text-gray-700 hover:bg-gray-100 whitespace-nowrap",title:`${e.url}\n(right-click to remove)`,children:[
23
23
  /* @__PURE__ */c("img",{src:`https://icons.duckduckgo.com/ip3/${new URL(e.url).hostname}.ico`,alt:"",className:"h-3.5 w-3.5",onError:e=>{e.target.style.visibility="hidden"}}),e.label]},t)),
24
24
  /* @__PURE__ */c("div",{className:"flex-1"}),
25
25
  /* @__PURE__ */c("button",{onClick:()=>{g(k);try{localStorage.setItem(u,k)}catch{}},className:"px-2 py-0.5 text-[11px] text-gray-500 hover:text-gray-800",title:"Set current page as homepage",children:"Set as home"})]}),
26
- /* @__PURE__ */c("div",{className:"flex-1 relative min-h-0 bg-gray-50",children:/* @__PURE__ */c(y,{url:k,iframeKey:W,iframeRef:O,openExternal:U,showHelp:H,dismissHelp:()=>A(!1)})})]})}function v(e){try{return new URL(e).hostname.replace(/^www\./,"")}catch{return e}}function y({url:e,iframeKey:t,iframeRef:o,openExternal:r,showHelp:n,dismissHelp:a}){const[d,u]=s(!1);i(()=>{u(!1)},[e]);return function(e){try{const t=new URL(e).hostname.toLowerCase();return k.some(e=>t===e||t.endsWith("."+e))}catch{return!1}}(e)&&!d?/* @__PURE__ */c("div",{className:"absolute inset-0 flex items-center justify-center bg-white p-8",children:/* @__PURE__ */l("div",{className:"max-w-md text-center",children:[
26
+ /* @__PURE__ */c("div",{className:"flex-1 relative min-h-0 bg-gray-50",children:/* @__PURE__ */c(y,{url:k,iframeKey:M,iframeRef:E,openExternal:F,showHelp:A,dismissHelp:()=>D(!1)})})]})}function v(e){try{return new URL(e).hostname.replace(/^www\./,"")}catch{return e}}function y({url:e,iframeKey:t,iframeRef:o,openExternal:r,showHelp:n,dismissHelp:a}){const[d,u]=s(!1);i(()=>{u(!1)},[e]);return function(e){try{const t=new URL(e).hostname.toLowerCase();return k.some(e=>t===e||t.endsWith("."+e))}catch{return!1}}(e)&&!d?/* @__PURE__ */c("div",{className:"absolute inset-0 flex items-center justify-center bg-white p-8",children:/* @__PURE__ */l("div",{className:"max-w-md text-center",children:[
27
27
  /* @__PURE__ */c("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,children:/* @__PURE__ */c("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"})}),
28
28
  /* @__PURE__ */l("h3",{className:"text-base font-semibold text-gray-800 mb-1",children:[v(e)," can't be embedded"]}),
29
29
  /* @__PURE__ */l("p",{className:"text-sm text-gray-500 mb-4",children:["This site sends an ",