efficient-shell 4.137.1 → 4.147.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 (57) hide show
  1. package/README.md +84 -13
  2. package/dist/{Browser-ZX5OTOKK.js → Browser-52FZDK3P.js} +14 -14
  3. package/dist/{Calculator-4SQGOQFQ.js → Calculator-4S44DK45.js} +22 -22
  4. package/dist/{ConfirmDialog-NPKW2XHM.js → ConfirmDialog-KJNNVFK7.js} +1 -1
  5. package/dist/{CurrencyConverter-2KOM7L6V.js → CurrencyConverter-TH6ISVZ5.js} +6 -6
  6. package/dist/{Documents-IZTIQYPG.js → Documents-TEV6WUCC.js} +15 -15
  7. package/dist/{Files-B63KKXGZ.js → Files-T3VMQAO3.js} +42 -42
  8. package/dist/{Notepad-WA77V45J.js → Notepad-LYNVGUHK.js} +12 -12
  9. package/dist/PdfViewer-CvLb2j_l.d.ts +40 -0
  10. package/dist/PdfViewer-ZCT3PYXU.js +1 -0
  11. package/dist/{PomodoroTimer-SDUBKXUS.js → PomodoroTimer-IEZ3ALBW.js} +40 -40
  12. package/dist/{Preview-D4KOA4OI.js → Preview-SJCMGRUS.js} +22 -22
  13. package/dist/{Sidebar-Y57LYXXI.js → Sidebar-CVF7C6BO.js} +3 -3
  14. package/dist/{Spreadsheet-JX5E6WH6.js → Spreadsheet-GR6GJTDP.js} +15 -15
  15. package/dist/{Stock-XAQ5WMUZ.js → Stock-MYXAUZJY.js} +3 -3
  16. package/dist/{Weather-HLAGJ5LV.js → Weather-HKCFNPV6.js} +14 -14
  17. package/dist/{WorldClock-IRI4VCTA.js → WorldClock-6XVXM4L2.js} +5 -5
  18. package/dist/apps/index.d.ts +3 -23
  19. package/dist/apps/index.js +1 -1
  20. package/dist/brand.css +1 -1
  21. package/dist/{chunk-466CM2TJ.js → chunk-4FS742BM.js} +1 -1
  22. package/dist/chunk-6JXLEPRE.js +1 -0
  23. package/dist/chunk-CVTOZCM3.js +24 -0
  24. package/dist/chunk-D4HWQ3ET.js +29 -0
  25. package/dist/{chunk-A2OQLLYK.js → chunk-I55UMU3R.js} +1 -1
  26. package/dist/{chunk-UZAJ5BF6.js → chunk-JNL7A5HC.js} +6 -6
  27. package/dist/chunk-KKOHUQPF.js +1 -0
  28. package/dist/{chunk-Y7BOLDRM.js → chunk-L7TEFXIS.js} +14 -14
  29. package/dist/{chunk-5CRAF2AD.js → chunk-MU5H2CDL.js} +2 -2
  30. package/dist/{chunk-H562R4GX.js → chunk-NUPXFPQW.js} +1 -1
  31. package/dist/{chunk-TAWKWSPN.js → chunk-NV35RTZK.js} +3 -3
  32. package/dist/{chunk-B43AGQET.js → chunk-SLCR26NK.js} +4 -4
  33. package/dist/{chunk-XO43FJRM.js → chunk-TFKB2WEB.js} +5 -5
  34. package/dist/chunk-TR6PLN7W.js +1 -0
  35. package/dist/{chunk-VVAGSKW7.js → chunk-UB22J6EF.js} +1 -1
  36. package/dist/{chunk-44UO7FYU.js → chunk-V453E4KU.js} +4 -4
  37. package/dist/{chunk-FQLMX3IE.js → chunk-ZFJH3P24.js} +448 -445
  38. package/dist/{chunk-JN4WXTWJ.js → chunk-ZR72F7TE.js} +51 -51
  39. package/dist/file-intake/index.js +1 -1
  40. package/dist/{index-DVI9DMSu.d.ts → index-DIQYwgxx.d.ts} +226 -20
  41. package/dist/index.d.ts +2 -2
  42. package/dist/index.js +235 -235
  43. package/dist/map/index.d.ts +266 -13
  44. package/dist/map/index.js +22 -20
  45. package/dist/page-tabs/index.js +12 -12
  46. package/dist/pdf/index.d.ts +9 -0
  47. package/dist/pdf/index.js +2 -0
  48. package/dist/ui/index.d.ts +1 -1
  49. package/dist/ui/index.js +1 -1
  50. package/dist/ui.css +7 -0
  51. package/package.json +6 -1
  52. package/dist/PdfViewer-RBRGEKWF.js +0 -1
  53. package/dist/chunk-3A46NCXK.js +0 -29
  54. package/dist/chunk-OQDWLHZI.js +0 -1
  55. package/dist/chunk-OWSAMA3I.js +0 -23
  56. package/dist/chunk-U4GTZPXP.js +0 -1
  57. package/dist/chunk-VEGMDXT4.js +0 -1
package/README.md CHANGED
@@ -357,6 +357,7 @@ is dragged. The side it ended up on is on the surface as `data-side`.
357
357
  | Action | `{ key, label, onSelect, icon?, danger?, disabled? }` | `<button role="menuitem">`. Every item written before the other kinds existed is one. |
358
358
  | Link | `{ key, label, href, icon?, current?, disabled? }` | A real `<a role="menuitem" href>`, so a middle-click, "Open in new tab" and the status bar's address work. `current` sets `aria-current="page"`. Choosing it closes the menu and lets the click through to the browser or the router. A disabled link has no `href`. |
359
359
  | Radio | `{ key, type: 'radio', label, checked, onSelect, icon?, disabled? }` | `<button role="menuitemradio" aria-checked>`, with a check on the checked one. Consecutive radios form one `role="group"`, named by a label directly above them. |
360
+ | Check box | `{ key, type: 'checkbox', label, checked, onSelect, icon?, disabled? }` | `<button role="menuitemcheckbox" aria-checked>`, a box ticked when on. Choosing it calls `onSelect` (the toggle — you flip `checked`) and the menu STAYS OPEN, focus where it was, so several are ticked in one visit; Escape, Tab or a click elsewhere puts it away. Consecutive check boxes form one `role="group"`, named by a label directly above them. Disable the rest when a limit is reached ("up to eight"). |
360
361
  | Label | `{ key, type: 'label', label }` | A heading (`PopupMenuLabel`). Not focusable. |
361
362
  | Divider | `{ key, type: 'divider' }` | `role="separator"` (`PopupMenuDivider`). |
362
363
 
@@ -368,6 +369,9 @@ trigger button's own classes, so it can look like your nav link or your button.
368
369
  It stays a `<button aria-haspopup="menu" aria-expanded>`, named by its content
369
370
  unless you pass `aria-label`. `menuClassName` is added to the menu's surface,
370
371
  and `minWidth` (pixels, or any CSS length; 180 by default) sets its narrowest.
372
+ `id` and `aria-describedby` go on the trigger, for a menu that is one field of a
373
+ form: the form's `<label htmlFor={id}>` names it (and pressing the label opens
374
+ it), and its hint or error describes it.
371
375
 
372
376
  **Keyboard** (the WAI-ARIA menu button). Enter, Space or ArrowDown on the
373
377
  trigger opens the menu on its first entry, and ArrowUp opens it on its last.
@@ -400,6 +404,7 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
400
404
  | `PopupMenu`, `PopupMenuItem`, `PopupMenuDivider`, `PopupMenuLabel`, `PopupSubmenu` | Right-click / context-menu primitive; `PopupSubmenu` is a row that opens a nested menu. |
401
405
  | `ShellContextMenu` | The shell-wide right-click menu, already mounted by `Layout` (`<Layout contextMenu={false}>` turns it off). Mount it yourself only on a screen rendered outside the layout. See [Right-click menu](#right-click-menu). |
402
406
  | `DropdownMenu` | A button and the menu it opens, right under it: actions, links (`href`, drawn by `linkAs`), radios, labels and dividers, with shared dismissal and the menu-button keyboard. `align` lines it up with the trigger's left or right edge; `side="top"` suits a trigger in a bottom action bar. See [Dropdown menus](#dropdown-menus). |
407
+ | `Dialog`, `Drawer` | A modal sheet that is not a shell window — centred (`Dialog`) or pinned to an edge (`Drawer`, `side` right / left / bottom) — for a till, a routed page or a form: trapped focus, a locked page, Escape and a backdrop click to close (`blocking` turns both off), a close cross when there is no `footer`. A `Dialog` is never taller than the screen less its gutter: a longer form — on a phone, most of them — scrolls inside the panel, footer included, and a footer wider than the panel wraps. `title` names it; `description` is a line under the title that DESCRIBES it to a screen reader — without one the body does, which suits a confirm whose body is the question and not a form, whose every label would be read out. `role="alertdialog"` (Dialog) for a confirmation that interrupts. A heading or note that lives in the body — a form that loads the record its heading names — names and describes it through `aria-labelledby` / `aria-describedby`. Two open at once — the second inside the first, or beside it on `<body>` where a host portals its dialogs — each hold focus, and only the one in front moves Tab (`useFocusTrap` keeps a stack). |
403
408
  | `ConfirmProvider`, `confirm` | Imperative `confirm({ title, body })` returning a Promise<boolean>. |
404
409
  | `GlobalSearch` | Cmd-K command palette. Pass `providers: SearchProvider[]` to add results. Portalled to `<body>` on the overlay layer (`Z_LAYERS.overlay`), so it covers every window, pinned or not; opening it closes any open popup (`dismissPopups`). |
405
410
  | `ShortcutHelp` | The keyboard cheatsheet shown on `?`. |
@@ -414,25 +419,26 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
414
419
  | `ProductionTimeline` | The scrubbable production bar, drawn on `TimelineTrack` inside the shared timeline card: a sentence-case heading with the PO as its subject, the window and the lead time as meta, a Play **pill** on the right whose glyph follows its state, and a footer carrying the status line and legend chips drawn with the track's own glyphs — a shipment is a diamond with a truck in it and an inspection a disc with a flask, on the rail and in the chip alike, because every kind is painted in the one accent and the glyph is the only thing left to tell them apart. The thumb only ever rests on a date a report was filed on — drag it and it snaps report to report, arrow it and it steps one report at a time — so the bar never shows an estimate dressed as a fact. Press Play and it **travels** between them: a fresh press rewinds to the first report, the thumb reads each snapshot for 700 ms and then glides to the next with the date chip counting the days it crosses, and the status line under the bar changes on arrival rather than while it is on its way. Pausing freezes it mid-glide and pressing Play again carries on from exactly there; touching the thumb ends the playback and hands it back. The walk keeps time by the clock rather than by the frame rate, so an idle or throttled pane does not stretch it. `onScrubProgress` forwards the track's in-flight fraction — `{ fromKey, toKey, t, ms }` keyed by report id, `null` at a stop — which is how a consumer's items table moves WITH the thumb instead of jumping a whole report's worth of quantities when it lands; it is optional and undefined by default, so a caller that only reads the snapshot needs no change. `onPickReport` selects a report; the optional `onOpenReport` / `onOpenMarker` make the label above the thumb a real button that opens the document behind it. `reportLabel` names a report wherever the bar states its identity — the caption over the thumb, the popover header, each dot's accessible name, the slider's value text and the status line — and defaults to its own `progress_number`, while `resetLabel` replaces the whole of the `Back to …` button: together they are what lets a portal that must not print an internal document number run the same card. A report carries an optional `preview` for its hover/focus popover — the stage row it filed, the note attached to it — and `renderReportPreview` builds one for the reports that carry none, the convenience for a caller whose list arrives straight off an API; the report's own preview wins where both exist, and the popover prints the report number and the date itself. `useProductionTimeline(opts)` owns the slider state and the derived snapshot so the caller's items table and the bar read the same state; `STAGES` / `calcOverall` / `calcReportOverall` are the stage maths it shares with that table. **`variant="rail"`** is the bar alone, for a window whose own header already says what the order is: no card, no heading, no lead-time line, no label under the active report — every report, shipment, inspection and **invoice** (kind `invoice`, a disc with a receipt) is a dot with a popover — the thumb with its date chip, `Start` and `Est. done` flanking the rail, the estimated completion as a hollow mark on the right edge while it is still ahead, and under the rail one row: Play, the return button when the thumb has been moved, and the `<report> · date · N% overall · N pc in stock` line. The rail spreads the reports (the track's `spread` axis) rather than folding two a week apart into a `×2`. `onPlayStart` fires before the thumb moves, on a fresh run and on a resume, so a window that keeps the bar in view while its table shows something else can switch the table first. |
415
420
  | `MilestoneTimeline` | The static sibling, on the same `TimelineTrack` and in the same card: one dot per milestone on a date axis, no scrubber. `heading` and `subject` split the title from the record it is about (`Mould development` · **001F/1813**), and the meta line states when it started, how long it has run and how many milestones are dated. The axis **cuts** rather than squeezes: a stretch holding more than 30% of the window's time becomes a fixed notch with a break glyph saying how many days it hides, and every stretch that still keeps time gets at least 48 px. Labels are packed into **two lanes** by their **measured** width, and a run of same-kind revisions folds into one `DFM ×4` pill whose members keep their dots on the rail: hovering it magnifies that stretch until each member can carry its own label, a click pins the popover listing them, Escape closes it. A milestone carries an optional `glyph` (a `default` one that names `doc` is drawn as the signed drawing it is), a `preview` for the popover, and an `onOpen` that the popover's "Open" button fires — `onClick` selects the dot on the bar, `onOpen` opens the drawing behind it, and a card that declares only the first keeps its Open button because the footer falls back to it. Label and date are printed by the popover above the preview, so a consumer supplies neither. A milestone with **no date gets no position**, because the only position available would be a date the record does not have: it is listed beside the bar under "Not yet reached" (two rows, then a real `+N more` button) and takes no part in the range, the fill or a phase bracket. `phase` groups of 2+ get a bracket marking parallel work, the fill never runs past today, and today gets its dashed rule and tag even when it IS the right edge — the commonest case, because a programme with no finish date ends at today. **`variant="rail"`** is the bar alone, for a window whose stepper already names the stages: no card, no heading, no label under any dot — every milestone is a dot with a popover, a revision carries a two-character `caption` over its dot (`v3`; captions that would overprint collapse to `v1–v4`), `edgeCaptions` flank the rail on its own row (`{ start: 'Start', end: 'Ready' }`), nothing is cut (the `spread` axis: every stretch at least 48 px, the idle ones drawn short rather than notched), and what has not happened is one dashed **cap** at the end of a dashed tail — a real button announced as the last undated milestone, naming the rest in its popover. The stepper and the rail then point at each other: `highlightKeys` lights one soft band from the leftmost named milestone to the rightmost and every dot inside it (a single key rings one dot; the cap answers to the pending milestone's key), and `onHoverChange` reports the milestone under the pointer or the focus, `null` on leave. A `provisional` milestone — a sample ORDERED, drawn where the shipment will be — is hollow, never "where we are", and outside the fill. |
416
421
  | `SettingRow` | One setting: its name and current value on one baseline, the explanation at full width beneath in a smaller voice. The value never shrinks, so a short badge keeps its line instead of folding into a narrow column. Handles a value that is a control (`controlId` labels it), a read-only fact, a value nobody knows (an em dash in the faint ink, never an empty cell), and a `quiet` row nothing can write yet. `DescriptionList` is the record-detail neighbour and stays that. |
417
- | `Button`, `Input`, `Textarea`, `Select`, `Checkbox`, `Radio`, `Switch`, `FormField`, `Label` | Form controls — controlled (`value`/`onChange`); `Input`/`Textarea` forward native props for react-hook-form. `Button` and `Switch` take `disabledReason`: persistent text beside the control wired with `aria-describedby`, never a `title` — a tooltip needs a hover a disabled control does not reliably get. `Switch` also takes `disabledReasonId`, so a panel whose controls are all dead for one reason states it once and points every control at it. A `Select` option carries an optional `description`, rendered as a second muted line **in the open list only** — the closed trigger stays one line, so one explained option cannot change the height of every field in the row. `NativeSelect` (and so `Select` on touch) ignores it: an `<option>` holds text and nothing else. On the desktop listbox, `title` joins `aria-label` / `aria-labelledby` / `aria-describedby` on the VISIBLE trigger button — it used to ride the native spread onto the `sr-only` `<select>`, where a tooltip has no pointer that can reach it and a fallback name sits on an `aria-hidden` element. `name`, `required` and the forwarded ref stay on the native control, which is what it is for. |
422
+ | `Button`, `Input`, `Textarea`, `Select`, `Checkbox`, `Radio`, `Switch`, `FormField`, `Label` | Form controls — controlled (`value`/`onChange`); `Input`/`Textarea` forward native props for react-hook-form. `Button` and `Switch` take `disabledReason`: persistent text beside the control wired with `aria-describedby`, never a `title` — a tooltip needs a hover a disabled control does not reliably get. `Switch` also takes `disabledReasonId`, so a panel whose controls are all dead for one reason states it once and points every control at it. A `Select` option carries an optional `description`, rendered as a second muted line **in the open list only** — the closed trigger stays one line, so one explained option cannot change the height of every field in the row. `NativeSelect` (and so `Select` on touch) ignores it: an `<option>` holds text and nothing else. An option also takes `lang` (a BCP 47 tag) when its label is written in another language than the page's — a language picker naming each language in its own words: it goes on the option in the open list, on the native `<option>` and on the closed trigger's label while chosen, so a screen reader and the font fallback treat each name as its own language (WCAG 3.1.2). On the desktop listbox, `title` joins `aria-label` / `aria-labelledby` / `aria-describedby` on the VISIBLE trigger button — it used to ride the native spread onto the `sr-only` `<select>`, where a tooltip has no pointer that can reach it and a fallback name sits on an `aria-hidden` element. `name`, `required` and the forwarded ref stay on the native control, which is what it is for. A `Checkbox` or `Radio` with both a `label` and a `description` is NAMED by the label and DESCRIBED by the description (`aria-labelledby` / `aria-describedby`); the whole row is still its `<label>`, so its words still tick it. |
418
423
  | `buttonClasses`, `iconButtonClasses` | `Button`'s and `IconButton`'s look as a class string, for something that must navigate and so cannot be a `<button>`: `<Link href="/organisations/new" className={buttonClasses({ variant: 'secondary' })}>Add a business</Link>`. Same options and defaults as the component (`variant`, `size`, `block`, `className`), and the components render through these functions, so the two cannot drift. What the string cannot give an `<a>`: a disabled state (`disabled:` never matches an anchor — drop the `href`, add `role="link"` and `aria-disabled="true"`, or render a disabled `Button`), and the cursor (an `<a href>` gets the browser's pointer, a `<button>` the host's preflight; set it once for both). Keep its `inline-flex` — ask for `block` rather than passing a display utility. An icon-only link still needs the `aria-label` `IconButton` would have required. Client references, like every kit function: call them in a `'use client'` file. |
424
+ | `Segmented` | A row of mutually exclusive options, all visible: a button group, or with `name` a real radio group that submits. Named by `label`, or by an element already on the page (`aria-labelledby`, a list row's own heading) and described by another (`aria-describedby`); an option's `title` says on hover what choosing it means. |
419
425
  | `RadioGroup` | One choice from a short visible list, with the group chrome each panel used to rebuild: a `role="radiogroup"` named by its own label (`aria-labelledby`, because `<label for>` cannot name a div), the `hint`/`error`/`required` wiring borrowed from `FormField`, and `orientation` for a row that wraps rather than compresses. The options are real native radios sharing one `name`, which is what gives the group its arrow keys, its disabled-skipping and its single tab stop — all from the user agent, none of it hand-rolled. `value: null` is "nothing chosen yet", not a silent first option. |
420
426
  | `Snippet` | A value the user copies rather than reads — an endpoint, a tenant id, a shell command. Monospace, truncated with the whole value on its `title` (`multiline` wraps instead), and a copy button that flips to a check for two seconds and says "Copied" through an `aria-live` region. `value` is what reaches the clipboard and `children` is what is shown, so a masked key displays masked and copies whole. `symbol` prints a leading glyph that is never copied and has **no default** — a URL has no `$` in front of it. A refused clipboard toasts rather than looking like it worked. Not a read-only `Input`: a value that can only be copied is not a control and should not take a tab stop or invite an edit. `id` and `aria-describedby` land on the COPY BUTTON — the only focusable thing in the row, and a labelable one — so a wrapping `FormField` really does label it and its hint is really announced; with `hideCopyButton` they fall back to the box, because an id that resolves to nothing is not wiring. |
421
427
  | `Kbd` | The badge the shortcut constants (`CMD_ENTER`, `ALT_SHIFT_E`, …) are shown in — a real `<kbd>`, two rungs, `className` layered on so a portal can tint it. It renders the string it is handed; which symbol a platform uses is what the constants already decide. |
422
428
  | `FormErrorSummary` | The error list at the top of a failed form (WCAG 3.3.1, the GOV.UK pattern): takes focus when errors appear, each message is a link that focuses the offending control by its `FormField` id. Renders nothing while `errors` is empty. |
423
429
  | `TagInput` | Multi-value field — chosen values as removable chips, typing filters the option list in the same dropdown SearchableSelect uses. `allowFreeText` admits unlisted entries; Backspace in the empty input removes the last chip. The value array stays duplicate-free by construction. |
424
- | `DatePicker`, `TimePicker`, `DateTimePicker`, `DateRangePicker` | Date/time fields, all careful to speak LOCAL dates and wall-clock times (never `toISOString`). `TimePicker` and `DateTimePicker` wrap the platform's own inputs in the kit's field styling; `TimePicker` hands back an `HH:MM` string — a time of day names no calendar day, so it never invents a Date. **`DatePicker`** opens the kit's `Calendar` in a popover (`native` swaps in the platform's date input). Its value posts from a hidden input that takes `name` and `form`; `required` marks the trigger `aria-required` and drops Clear from the footer (`clearable` overrides either way); Today is offered only when `min`/`max` allow today; `aria-*`, `data-*`, `title`, `tabIndex`, `autoFocus`, `onFocus` and `onBlur` reach the trigger, while what only a text box can use (`readOnly`, `pattern`, `autoComplete`…) applies with `native` alone. Named by a `<label htmlFor={id}>` or `aria-labelledby`, the trigger is described by the date spelled out. A browser never validates a hidden input, so `required` cannot block a submit in the popover. **`DateRangePicker`** is a from/to calendar with presets in one popover: side by side on a desktop; where that does not fit the room the box allows (a phone, a narrow shell window — measured, not a media query), the panel takes the full width between the margins and stacks, the presets a wrapping row of chips above the calendar and From/To one line each. `fullWidth` fills a filter-grid cell instead of shrink-wrapping its label. `id` (on the trigger button, for `<label htmlFor>`), `aria-labelledby` and `aria-label` name the trigger, and the chosen range is then its description ("Period, button, 1 August 2026 to 3 October 2026"). `name={{ from, to }}` posts the applied range as two hidden `YYYY-MM-DD` inputs (`''` when unset), so a GET form or a server action reads it as it read two date inputs. `required` adds `aria-required` and, unless `clearable` is given, removes Clear. Both pickers take `weekStartsOn` (0 Sunday, 1 Monday); without it the week starts where the provider's `locale` says. |
430
+ | `DatePicker`, `TimePicker`, `DateTimePicker`, `DateRangePicker` | Date/time fields, all careful to speak LOCAL dates and wall-clock times (never `toISOString`). `TimePicker` and `DateTimePicker` wrap the platform's own inputs in the kit's field styling; `TimePicker` hands back an `HH:MM` string — a time of day names no calendar day, so it never invents a Date. **`DatePicker`** opens the kit's `Calendar` in a popover (`native` swaps in the platform's date input). Its value posts from a hidden input that takes `name` and `form`; `required` adds "Required" to the trigger's description (`datePicker.required`; a button may not carry `aria-required`) and drops Clear from the footer (`clearable` overrides either way); `invalid` paints the field, and the reader hears the error message the host links through `aria-describedby` (`FormField` does) — the trigger carries no `aria-invalid`, which a button may not either; Today is offered only when `min`/`max` allow today; `aria-*`, `data-*`, `title`, `tabIndex`, `autoFocus`, `onFocus` and `onBlur` reach the trigger, while what only a text box can use (`readOnly`, `pattern`, `autoComplete`…) applies with `native` alone. Named by a `<label htmlFor={id}>` or `aria-labelledby`, the trigger is described by the date spelled out. A browser never validates a hidden input, so `required` cannot block a submit in the popover. **`DateRangePicker`** is a from/to calendar with presets in one popover: side by side on a desktop; where that does not fit the room the box allows (a phone, a narrow shell window — measured, not a media query), the panel takes the full width between the margins and stacks, the presets a wrapping row of chips above the calendar and From/To one line each. `fullWidth` fills a filter-grid cell instead of shrink-wrapping its label. `id` (on the trigger button, for `<label htmlFor>`), `aria-labelledby` and `aria-label` name the trigger, and the chosen range is then its description ("Period, button, 1 August 2026 to 3 October 2026"). `name={{ from, to }}` posts the applied range as two hidden `YYYY-MM-DD` inputs (`''` when unset), so a GET form or a server action reads it as it read two date inputs. `required` adds "Required" to the trigger's description (`dateRange.required`) and, unless `clearable` is given, removes Clear. `min` / `max` (`YYYY-MM-DD`) bound it as they bound `DatePicker`: days outside are disabled, a preset whose range reaches outside is not offered (none at all on a range that starts today — time off, a booking — and then no Custom either), and with nothing chosen the panel opens on the first month that has a day to pick. Both pickers take `weekStartsOn` (0 Sunday, 1 Monday); without it the week starts where the provider's `locale` says. |
425
431
  | `Card`, `StatCard` | Surface panel (optional header/footer) + dashboard metric tile. |
426
432
  | `Avatar`, `AvatarGroup` | User avatar with initials fallback + status dot; overlapping stack with +N overflow. |
427
- | `Banner` | Static in-flow alert (`tone`: info / success / warning / danger). `action` renders a control at the right edge — after the text, so a screen reader reaches the problem before the remedy, and before the dismiss ×, so "Update card" is never past "close this". Vertically centred against the whole box and `shrink-0`, so a message that wraps does not drag the button down or squeeze it. A banner that names a condition and leaves the fix three menus away is the usual shape of a notice nobody acts on. |
433
+ | `Banner` | Static in-flow alert (`tone`: info / success / warning / danger). `action` renders a control at the right edge — after the text, so a screen reader reaches the problem before the remedy, and before the dismiss ×, so "Update card" is never past "close this". Vertically centred against the words and `shrink-0`, so a message that wraps does not drag the button down or squeeze it — and on a narrow banner (a phone, a narrow column: the words keep at least 12rem) it drops UNDER the words rather than leave them a column one word wide. A banner that names a condition and leaves the fix three menus away is the usual shape of a notice nobody acts on. |
428
434
  | `Tabs`, `Accordion`, `Tooltip` | Controlled tab strip, collapsible sections, frosted hover/focus tooltip. A tooltip's `side` is a preference: near an edge the bubble slides along that side to stay 8 px clear of it, and flips to the opposite side when its own has no room, keeping inside the screen and inside the shell window that owns its trigger (UI-11). It never leaves that box: a bubble wider than the room wraps, and one taller than the room is cut off at it. A small arrow on the edge facing the trigger points at the trigger's centre wherever the bubble ended up, kept clear of the bubble's corners; it is the bubble's own surface (`.rosh-tooltip-arrow` in `ui.css`), solid and seamless under `rosh-reduce-transparency`, and hidden from assistive technology. |
429
435
  | `Stepper` | A linear wizard's progress strip — numbered circles, connectors, `aria-current="step"`. Controlled like Tabs; completed steps are clickable to go back (when `onChange` is wired), upcoming steps never are — moving forward belongs to the wizard's own Continue button, behind its validation. |
430
- | `DataTable` | Read-only table for server-driven lists: it renders and reports, and never sorts or pages its own `data`. Controlled sort and pager, row selection, column groups, pinned columns, windowed rendering. `stackOnNarrow` turns each row into a labelled card on a narrow container — see [DataTable on a narrow screen](#datatable-on-a-narrow-screen). |
436
+ | `DataTable` | Read-only table for server-driven lists: it renders and reports, and never sorts or pages its own `data`. Controlled sort and pager, row selection, column groups, pinned columns, windowed rendering. `onRow` gives a row `onClick`, a `className` and any `data-*` attributes for its `<tr>` — a test's or a stylesheet's hook (`data-testid`, `data-state`). `stackOnNarrow` turns each row into a labelled card on a narrow container — see [DataTable on a narrow screen](#datatable-on-a-narrow-screen). |
431
437
  | `Pagination` | Numbered page control (pairs with tables; complements `ListFooter`). |
432
438
  | `Sparkline`, `LineChart`, `BarChart`, `DonutChart`, `ScatterChart` | Dependency-free inline-SVG charts (`currentColor`-themed). `LineChart` is the multi-series trend with optional scale, legend, dots and area fill. `ScatterChart` takes `xDomain` / `yDomain` to override the derived axis, and `xScale` / `yScale` of `'log'` for a long tail; a point outside a supplied domain is dropped and counted in the accessible label. |
433
439
  | `BulkImportGrid` | Paste-or-upload bulk entry with column mapping, duplicate review and optional sum-merge. Hands resolved rows to `onImport`; owns no persistence. The whole panel takes a dropped CSV; the Upload CSV button opens the same dialog. |
434
440
  | `UndoProvider`, `UndoControls` | One undo stack per form window, covering its fields, line items and bulk imports. Register state with `useUndoable` and the shell shows the Undo/Redo pair itself, leftmost in the window footer, as soon as the form has state and the window is editing (a draft, a duplicate, Edit mode) or the form has said `useUndoCanEdit(true)` — a view that merely holds state gets nothing — it joins a footer that is there for other reasons and never creates one, and the mobile chrome (footer hidden) gets nothing. A form mounts `UndoControls` by hand only for a place the footer is not (a plain overlay, a section header); a hand mount claims the stack and the shell then mounts nothing for it, so a form shows one pair wherever it chose to put it. Binds ⌘Z / ⇧⌘Z (and Ctrl+Y) except while the caret is in a field, where the browser's own undo wins. `WindowManager` already mounts one per window, scoped with `windowId` so a keypress reaches only the frontmost window; pass `windowId` yourself for any provider you mount outside a `<Modal>`, or two open windows will step back together. Offered to anyone who may edit the record — gate with `canEdit` and/or `perms`; a reader sees no controls and records no history. The shell-level provider cannot know the record's permissions, so a read-only form nests its own `<UndoProvider canEdit={false}>` to shadow it. A provider nested inside a window puts its own pair in the footer on the same footer terms — it joins a footer that is there for other reasons, never creates one or holds it open, and shows nothing on the mobile chrome — but its `canEdit` takes the place of the editing check: the form that nested it has already said whether the record may be edited, so pass `canEdit={isEditing}` (or the lock) rather than leaving the default. |
435
- | `Calendar` | Month grid with full keyboard navigation (arrows, Home/End, PageUp/Down) and `role="grid"` semantics. Single or range. The shared grid behind `DatePicker` and `DateRangePicker`. `weekStartsOn` defaults to the provider locale's first day of the week (`firstDayOfWeek`), Sunday without one. |
441
+ | `Calendar` | Month grid with full keyboard navigation (arrows, Home/End, PageUp/Down) and `role="grid"` semantics. Single or range. Days of the months either side are quieter than the month's own but keep AA contrast: they are dates a person can press. The shared grid behind `DatePicker` and `DateRangePicker`. `weekStartsOn` defaults to the provider locale's first day of the week (`firstDayOfWeek`), Sunday without one. |
436
442
  | `DashboardTemplate`, `DataTablePage`, `FormLayoutPage`, `CheckoutTemplate`, `EmailTemplate`, `ChatTemplate`, `GalleryTemplate`, `AuthScreen`, `ErrorPage` | Zero-prop starter page templates composed from the primitives. |
437
443
  | `ErrorBoundary` | Catches a render crash and shows `ErrorPage` 500 rather than a blank screen. `showDetails` is off by default, so a visitor is never shown the stack; the fallback is `role="alert"`. Takes `onError` for reporting and `resetKeys` to recover on navigation. |
438
444
  | `AppUpdateBanner` | "A new version is available" for a tab that outlived a deploy, without a service worker. Polls the build's `version.json` (a minute apart from a visible tab, never from a hidden one, and once more the moment a hidden tab is shown) and, when the deployed version differs from `currentVersion`, pins a warning banner at the top centre above every window naming both versions with a primary "Refresh now" that reloads. Renders nothing until then. `versionUrl`, `pollMs`, `message`, `onRefresh`. Pair it with `installStaleChunkReload()` for the tab that opens a lazy window before it notices. |
@@ -491,11 +497,13 @@ All exports are named — `import { Modal, ... } from 'efficient-shell'`.
491
497
  app without windows gives `PdfActionButton` an `openPreview` callback that
492
498
  mounts the shell's `Preview` somewhere of its own — a tab, a route, an overlay.)
493
499
  When the document belongs *inside* something else — a preview beside the form that
494
- generates it, a document tab on a record — render `PdfViewer` directly:
500
+ generates it, a document tab on a record, a dialog — render `PdfViewer` directly.
501
+ A host without the window manager takes it from **`efficient-shell/pdf`** (see
502
+ below); a desktop host may also take it from `efficient-shell/apps`:
495
503
 
496
504
  ```tsx
497
- import { lazy, Suspense } from 'react';
498
- const PdfViewer = lazy(() => import('efficient-shell/apps').then(m => ({ default: m.PdfViewer })));
505
+ import { Suspense } from 'react';
506
+ import { PdfViewer } from 'efficient-shell/pdf'; // already lazy
499
507
 
500
508
  <div className="h-[32rem]">
501
509
  <Suspense fallback={<p>Loading viewer…</p>}>
@@ -511,6 +519,12 @@ const PdfViewer = lazy(() => import('efficient-shell/apps').then(m => ({ default
511
519
  | `fit` | `'width'` (default) fills the container's width and lets a tall page scroll — right for a viewer that owns a window. `'page'` fits the whole page, which is what a pane inside a dialog wants. Either way it is the *initial* mode: the reader can still zoom, and the Fit button re-arms it. |
512
520
  | `onDownload` | Replaces the built-in "save `url` as `filename`". |
513
521
  | `onEmail` | Adds an Email button to the toolbar. Omit it and there is none. |
522
+ | `workerSrc` | Where the host serves `pdfjs-dist/build/pdf.worker.min.mjs` (the installed version's). Omit it and the worker comes from unpkg — which a host whose Content-Security-Policy allows its own scripts only cannot load. It sets pdf.js's global `GlobalWorkerOptions.workerSrc`, so give every viewer on the page the same value. |
523
+ | `wasmUrl` | Where the host serves the installed `pdfjs-dist/wasm/` directory (the JBIG2 / JPEG 2000 decoders, ICC colour, and the JavaScript fallbacks pdf.js loads when WebAssembly is refused). Wins over `window.__REACT_OS_SHELL_PDF_WASM__`; omit both and the bundler-emitted decoders are used. |
524
+
525
+ Its words — the page counter, Zoom, Fit, Print, Download, the loading line and
526
+ the two toasts — come from the strings catalog's `pdf` section, so a host that
527
+ mounts `ShellStringsProvider` gets them in its reader's language.
514
528
 
515
529
  It fills its parent and scrolls inside, so **give the parent a resolved
516
530
  height** — `h-full` inside a flex column, or an explicit height. It brings its
@@ -533,6 +547,52 @@ The alternative this replaces is `<iframe src={objectUrl}>`, which hands the
533
547
  document to the browser's own plugin — its toolbar, its thumbnail rail, its
534
548
  idea of the zoom, none of it themeable and none of it testable.
535
549
 
550
+ #### The PDF reader without the window manager — `efficient-shell/pdf`
551
+
552
+ `efficient-shell/apps` is the bundled window registry: importing it puts every
553
+ bundled app's chunk in your build graph — react-router-dom,
554
+ `@tanstack/react-query` and Heroicons statically, `xlsx`, `dxf-viewer` and
555
+ `online-3d-viewer` behind dynamic imports. Vite never complains about an
556
+ optional peer it is not asked to load; **Next.js (Turbopack) and webpack resolve
557
+ every import while they build**, so a server-rendered app that wanted one PDF in
558
+ a dialog failed its build on five packages it would never load.
559
+ `efficient-shell/pdf` is `PdfViewer` alone: React, and pdf.js behind the lazy
560
+ boundary — `scripts/verify-dist.mjs` holds the built graph to exactly that.
561
+
562
+ ```bash
563
+ npm i pdfjs-dist
564
+ ```
565
+
566
+ ```tsx
567
+ 'use client';
568
+ import { Suspense } from 'react';
569
+ import { Dialog } from 'efficient-shell/ui';
570
+ import { PdfViewer } from 'efficient-shell/pdf';
571
+
572
+ <Dialog open={open} onClose={close} title="INV-000123" size="xl">
573
+ <div className="h-[75vh]">
574
+ <Suspense fallback={null}>
575
+ <PdfViewer
576
+ url="/api/invoices/123/pdf"
577
+ filename="INV-000123.pdf"
578
+ workerSrc="/vendor/pdfjs/6.3.289/pdf.worker.min.mjs"
579
+ wasmUrl="/vendor/pdfjs/6.3.289/wasm/"
580
+ />
581
+ </Suspense>
582
+ </div>
583
+ </Dialog>
584
+ ```
585
+
586
+ The host serves pdf.js's worker and, for scanned documents, its `wasm/`
587
+ directory, from its own origin and under the installed version (so an upgrade
588
+ cannot pair a cached old worker with a new main thread) — copied from
589
+ `node_modules/pdfjs-dist` by a vendor script, or a static route. Its CSP then
590
+ needs `worker-src 'self'` and nothing from a CDN. pdf.js 6 compiles nothing
591
+ with `eval`; a CSP without `'wasm-unsafe-eval'` makes it load the decoders'
592
+ JavaScript fallbacks from `wasmUrl` instead of the WebAssembly ones.
593
+
594
+ `Dialog`'s `size="xl"` is the width for reading a page in.
595
+
536
596
  ### UI kit without the window manager — `efficient-shell/ui`
537
597
 
538
598
  Not every app that wants this package's components wants a desktop. A
@@ -931,7 +991,7 @@ tables read); the sample content of the starter templates (`DashboardTemplate`,
931
991
  `AuthScreen` and the rest are demos to copy, not components to configure); and
932
992
  console warnings, which are for developers.
933
993
 
934
- **The full catalog** — 39 sections, 359 keys. A function's default is shown
994
+ **The full catalog** — 39 sections, 362 keys. A function's default is shown
935
995
  with sample arguments.
936
996
 
937
997
  <details><summary><code>window</code> — 17 keys</summary>
@@ -1122,7 +1182,7 @@ with sample arguments.
1122
1182
 
1123
1183
  </details>
1124
1184
 
1125
- <details><summary><code>datePicker</code> — 4 keys</summary>
1185
+ <details><summary><code>datePicker</code> — 5 keys</summary>
1126
1186
 
1127
1187
  | Key | Type | English default |
1128
1188
  |---|---|---|
@@ -1130,10 +1190,11 @@ with sample arguments.
1130
1190
  | `dialog` | `string` | `Choose a date` |
1131
1191
  | `clear` | `string` | `Clear` |
1132
1192
  | `today` | `string` | `Today` |
1193
+ | `required` | `string` | `Required` |
1133
1194
 
1134
1195
  </details>
1135
1196
 
1136
- <details><summary><code>dateRange</code> — 19 keys</summary>
1197
+ <details><summary><code>dateRange</code> — 20 keys</summary>
1137
1198
 
1138
1199
  | Key | Type | English default |
1139
1200
  |---|---|---|
@@ -1147,6 +1208,7 @@ with sample arguments.
1147
1208
  | `fromOnly` | `(date: string) => string` | `From {date}` |
1148
1209
  | `toOnly` | `(date: string) => string` | `To {date}` |
1149
1210
  | `range` | `(from: string, to: string) => string` | `{from} to {to}` |
1211
+ | `required` | `string` | `Required` |
1150
1212
  | `last2Weeks` | `string` | `Last 2 Weeks` |
1151
1213
  | `lastMonth` | `string` | `Last Month` |
1152
1214
  | `last3Months` | `string` | `Last 3 Months` |
@@ -1810,14 +1872,23 @@ copying both from `node_modules/maplibre-gl/dist`). Tiles come from
1810
1872
 
1811
1873
  | Export | Notes |
1812
1874
  |---|---|
1813
- | `EntityMap` | The map. `pins` (`MapPin`: glyph or `icon` paths, `badge`, `tone` classes, `approximate`, `faded`), `selectedKey` + `onSelect`, `focus` to fly to a pin, `routes` (`MapRoute`, dashed when an estimate), `fitTo`, `attribution`. Follows `data-theme="dark"` or a `dark` class on `<html>`; resizes with its box (a shell window sends no window resize). Without WebGL it says so and its children still draw. |
1875
+ | `EntityMap` | The map. `pins` (`MapPin`: glyph, `icon` paths or an `iconNode` React element, `badge`, `tone` classes — a border colour among them replaces the white edge — `data` for a host stylesheet's `data-*` key, `approximate`, `faded`, `stop` for a route's numbered stop, `dot` for a small context dot on a full-size target; pass a memoised array: pins are redrawn only when it changes, so the focus stays on one), `selectedKey` + `onSelect`, `focus` to fly to a pin, `routes` (`MapRoute`, dashed when an estimate, `color` any CSS colour, `attribution` a credit shown while it is drawn), `marks` (`MapMark`: a start, an end, a place on the route — named images, never pressed), `fitTo` (pins and marks), `attribution`, `initialView`, `cooperativeGestures` (`'coarse'` for touch screens). Follows `data-theme="dark"` or a `dark` class on `<html>`; resizes with its box (a shell window sends no window resize). Without WebGL it says so and its children still draw. |
1876
+ | `EntityMap` `groupCrowded` | Every pin a target of 24 px or more (a stop 28) and no two targets touching — WCAG 2.5.8, which axe's `target-size` measures and a bigger hit area alone does not satisfy where pins crowd (axe 4.13 even throws on a target several others cover, reporting the rule as `incomplete`). Pins that would touch are one group ("5 leads here", `noun` names them; stops only with stops, "Stops 3–4"), which zooms in until they stand apart and hands the focus to the first — as close as the map goes, it chooses the next of them. A pin that would sit under a stop, or crowd a control over the map (the zoom buttons, the attribution `<summary>`, the card's buttons), is left off until the map moves. `focus` zooms until the pin stands alone. |
1877
+ | `EntityMap` `card` | The chosen pin's card, beside its pin and pointing at it — above where it fits, else below, turned to a corner by the map's sides, the map moving by what is missing (`placeCard`) — never taller than the map can show beside its pin (it scrolls). On a map under `cardMinMapWidth` (480 px) it runs along the bottom edge. Closed from the keyboard, the focus goes back to its pin. |
1878
+ | `EntityMap` `highlightKey`, `useMapHighlight` | Rings what the list beside the map points at — its pin, the group holding it, a mark, or its own spot while it is left off. A picture: `aria-hidden`, nothing to press, and the map never moves for it. `pointAt(key)` is the row's pointer and focus handlers. |
1879
+ | `EntityMap` `picking` + `onPick`, `fullScreen` | The next click on the map as `{ lat, lng }` (a crosshair meanwhile). `fullScreen: { active, onChange }` draws "Full screen" / "Leave full screen" over the top-left corner; the host lays the page out for `active`, the map asks the browser to match and reports when the browser leaves it. |
1814
1880
  | `LocationsMap` | A record's compact map over an endpoint: legend, "Locating N addresses…", a note per record with no pin, the chosen pin's card. `palette` names the categories the portal's way; `numbers` gives each pin the number the page's own list shows. |
1815
1881
  | `MapLegend`, `SelectedCard` | The overlays, for a map that lays out its own list (the admin portal's list-wide Map view). |
1816
1882
  | `useMapPoints(queryKey, queryFn)` | react-query, re-asked every few seconds while `pending > 0`; `mapPointsRefetchInterval` is the same rule for a hand-written query. |
1817
1883
  | `MAP_CATEGORIES`, `pinTone`, `pinGlyph`, `pinCategory`, `categoryLabel`, `categoryTone` | The pin palette. Importer and supplier are **sky**, never blue — the themes remap `bg-blue-600` to the accent. |
1818
1884
  | `isOnMap`, `placementNote`, `pointKey`, `locationNumbers`, `summarize`, `summaryText`, `legendCategories`, `offMapNotes` | The pure logic over `MapPoint[]`. |
1885
+ | `layoutPins`, `zoomApart`, `stopsLabel`, `placeCard`, `cardMaxHeight` | The pure rules behind `groupCrowded` and `card`. |
1819
1886
  | `loadMapLibre`, `mapLibreWorkerUrl`, `foldAttributionOnce` | For a host drawing its own MapLibre layer on the same terms. |
1820
1887
 
1888
+ Every word the map draws on its own account — a group's name, "Full screen",
1889
+ the could-not-draw line, `SelectedCard`'s buttons — is the `map` section of
1890
+ `ShellStrings`.
1891
+
1821
1892
  ### Misc
1822
1893
 
1823
1894
  | Export | Notes |
@@ -1833,7 +1904,7 @@ copying both from `node_modules/maplibre-gl/dist`). Tiles come from
1833
1904
  | `GROUP_COLORS`, `GROUP_COLORS_SOLID`, `groupColors(group, emphasis?)` | The status palette, both registers, as the class strings the badges emit — for a surface that has to build its own pill (a virtualised cell, a canvas legend) and must not guess at the colors. |
1834
1905
  | `severityOf(value, warn?, crit?)` | The `SeverityTone` (`success` \| `warning` \| `danger`) a reading earns against **inclusive** bounds; `null` when there's no reading or no usable bounds — the shell hardcodes no threshold. Backs `MetricBar`; use it to roll a `SidebarNavItem severity` up. |
1835
1906
  | `isSeverityTone(value)` | Type guard for the three tones. Validate a backend rollup with it at the fetch boundary, where a bad token can still be reported against its payload, rather than letting it surface as a wrong pixel. |
1836
- | `toast.success / .error / .info` | Toast notifications — auto-mounts container. |
1907
+ | `toast.success / .error / .info` | Toast notifications — auto-mounts container. Each plays the user's chime; `toast.configure({ sound: false })` keeps them quiet for a host whose screen makes sounds of its own (an alarm, a till's beep), and `{ sound: true }` on one toast still asks for it. |
1837
1908
  | `Z_LAYERS` | The shell's z-index scale: `window` 50 (+ `windowStep` 10 per window above), `menu` 400, `pinnedWindow` 999, `overlay` 9999 (the palette, `Dialog`, `Drawer`, the shortcut sheet), `popup` 10000 (portalled listboxes, calendars, the server-status card). Popups sit above overlays so one opened inside a dialog is not drawn behind it. Apply as inline `zIndex` — an interpolated `z-[…]` class is invisible to Tailwind's scan. |
1838
1909
  | `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. |
1839
1910
  | `Kbd` constants — `MOD`, `ALT`, `SHIFT`, `ENTER`, `ALT_SHIFT_E`, `CMD_K`, … | Symbol constants for rendering keyboard shortcuts. |
@@ -1,29 +1,29 @@
1
- import{peekBrowserStartUrl as e,claimBrowserStartUrl as t}from"./chunk-LWLCNQY7.js";import{AboutApp as o}from"./chunk-TAWKWSPN.js";import{WindowTitle as r}from"./chunk-JN4WXTWJ.js";import"./chunk-UB4JIOLD.js";import"./chunk-U4GTZPXP.js";import"./chunk-44UO7FYU.js";import"./chunk-PH3PRIFP.js";import"./chunk-UZAJ5BF6.js";import{confirm as n}from"./chunk-3A46NCXK.js";import"./chunk-57MIENLJ.js";import"./chunk-OQDWLHZI.js";import"./chunk-MAIHBBWT.js";import"./chunk-D65T4H6J.js";import{useRef as a,useEffect as i,useState as l}from"react";import{jsxs as s,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]=l(x),[k,w]=l(h||p),[N,L]=l(k),[C,j]=l([k]),[z,M]=l(0),[W,B]=l(0),[H,S]=l(f),[A,R]=l(!1),[E,I]=l(null),P=a(null),T=a(null);i(()=>{try{localStorage.setItem(d,JSON.stringify(H))}catch{}},[H]);const U=e=>{const t=b(e);t&&(w(t),L(t),j(e=>{const o=e.slice(0,z+1);return o.push(t),o}),M(e=>e+1),R(!1))},D=()=>{"undefined"!=typeof window&&window.open(k,"_blank","noopener,noreferrer")},O=()=>{const e=E?.trim();e?(S(t=>[...t,{label:e,url:k}]),I(null)):I(null)};i(()=>{if(null===E)return;P.current?.focus(),P.current?.select();const e=e=>{const t=e.target;t?.closest("[data-bookmark-popover]")||I(null)},t=setTimeout(()=>document.addEventListener("mousedown",e),0);return()=>{clearTimeout(t),document.removeEventListener("mousedown",e)}},[null!==E]);const J=e=>{S(t=>t.filter((t,o)=>o!==e))},F=z>0,q=z<C.length-1,K=H.some(e=>e.url===k),V="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-NV35RTZK.js";import"./chunk-6JXLEPRE.js";import{WindowTitle as r}from"./chunk-ZR72F7TE.js";import"./chunk-V453E4KU.js";import"./chunk-UB4JIOLD.js";import"./chunk-PH3PRIFP.js";import"./chunk-JNL7A5HC.js";import{confirm as n}from"./chunk-D4HWQ3ET.js";import"./chunk-57MIENLJ.js";import"./chunk-MAIHBBWT.js";import"./chunk-TR6PLN7W.js";import"./chunk-D65T4H6J.js";import{useRef as a,useEffect as i,useState as l}from"react";import{jsxs as s,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]=l(x),[k,w]=l(h||p),[N,L]=l(k),[C,j]=l([k]),[z,M]=l(0),[R,W]=l(0),[E,H]=l(f),[B,S]=l(!1),[A,T]=l(null),D=a(null),I=a(null);i(()=>{try{localStorage.setItem(d,JSON.stringify(E))}catch{}},[E]);const P=e=>{const t=b(e);t&&(w(t),L(t),j(e=>{const o=e.slice(0,z+1);return o.push(t),o}),M(e=>e+1),S(!1))},U=()=>{"undefined"!=typeof window&&window.open(k,"_blank","noopener,noreferrer")},J=()=>{const e=A?.trim();e?(H(t=>[...t,{label:e,url:k}]),T(null)):T(null)};i(()=>{if(null===A)return;D.current?.focus(),D.current?.select();const e=e=>{const t=e.target;t?.closest("[data-bookmark-popover]")||T(null)},t=setTimeout(()=>document.addEventListener("mousedown",e),0);return()=>{clearTimeout(t),document.removeEventListener("mousedown",e)}},[null!==A]);const O=e=>{H(t=>t.filter((t,o)=>o!==e))},V=z>0,q=z<C.length-1,F=E.some(e=>e.url===k),K="p-1.5 rounded hover:bg-gray-200 transition-colors text-gray-600 disabled:opacity-30";/* @__PURE__ */
2
2
  return s("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__ */s("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(C[e]),L(C[e]),R(!1)}},disabled:!F,className:V,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<C.length-1){const e=z+1;M(e),w(C[e]),L(C[e]),R(!1)}},disabled:!q,className:V,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:()=>B(e=>e+1),className:V,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:()=>U(p),className:V,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
- /* @__PURE__ */c("form",{onSubmit:e=>{e.preventDefault(),U(N)},className:"flex-1 flex items-center mx-1",children:/* @__PURE__ */s("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:[
6
+ /* @__PURE__ */c("button",{onClick:()=>{if(z>0){const e=z-1;M(e),w(C[e]),L(C[e]),S(!1)}},disabled:!V,className:K,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<C.length-1){const e=z+1;M(e),w(C[e]),L(C[e]),S(!1)}},disabled:!q,className:K,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:K,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:()=>P(p),className:K,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
+ /* @__PURE__ */c("form",{onSubmit:e=>{e.preventDefault(),P(N)},className:"flex-1 flex items-center mx-1",children:/* @__PURE__ */s("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:()=>K?J(H.findIndex(e=>e.url===k)):void I(v(k)),className:V+" "+(K?"text-yellow-500":""),title:K?"Remove bookmark":"Add bookmark",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:K?"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:D,className:V,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:()=>R(e=>!e),className:V,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__ */s("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:()=>F?O(E.findIndex(e=>e.url===k)):void T(v(k)),className:K+" "+(F?"text-yellow-500":""),title:F?"Remove bookmark":"Add bookmark",children:/* @__PURE__ */c("svg",{className:"h-4 w-4",fill:F?"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:K,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:()=>S(e=>!e),className:K,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!==A&&/* @__PURE__ */s("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:P,type:"text",value:E,onChange:e=>I(e.target.value),onKeyDown:e=>{"Enter"===e.key?O():"Escape"===e.key&&I(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:D,type:"text",value:A,onChange:e=>T(e.target.value),onKeyDown:e=>{"Enter"===e.key?J():"Escape"===e.key&&T(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__ */s("div",{className:"flex justify-end gap-1 mt-2",children:[
20
- /* @__PURE__ */c("button",{onClick:()=>I(null),className:"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded",children:"Cancel"}),
21
- /* @__PURE__ */c("button",{onClick:O,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__ */s("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===H.length&&/* @__PURE__ */c("span",{className:"text-[11px] text-gray-400 italic px-2",children:"No bookmarks yet — star the address bar to add one."}),H.map((e,t)=>/* @__PURE__ */s("button",{onClick:()=>U(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"})&&J(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:()=>T(null),className:"px-2 py-1 text-xs text-gray-600 hover:bg-gray-100 rounded",children:"Cancel"}),
21
+ /* @__PURE__ */c("button",{onClick:J,disabled:!A.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__ */s("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===E.length&&/* @__PURE__ */c("span",{className:"text-[11px] text-gray-400 italic px-2",children:"No bookmarks yet — star the address bar to add one."}),E.map((e,t)=>/* @__PURE__ */s("button",{onClick:()=>P(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"})&&O(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:T,openExternal:D,showHelp:A,dismissHelp:()=>R(!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]=l(!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__ */s("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:R,iframeRef:I,openExternal:U,showHelp:B,dismissHelp:()=>S(!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]=l(!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__ */s("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__ */s("h3",{className:"text-base font-semibold text-gray-800 mb-1",children:[v(e)," can't be embedded"]}),
29
29
  /* @__PURE__ */s("p",{className:"text-sm text-gray-500 mb-4",children:["This site sends an ",
@@ -1,29 +1,29 @@
1
- import{loadAppearance as e,WidgetSettingsModal as t}from"./chunk-H562R4GX.js";import{useWidgetSettings as n}from"./chunk-JN4WXTWJ.js";import"./chunk-UB4JIOLD.js";import"./chunk-44UO7FYU.js";import"./chunk-PH3PRIFP.js";import"./chunk-UZAJ5BF6.js";import"./chunk-3A46NCXK.js";import"./chunk-57MIENLJ.js";import"./chunk-OQDWLHZI.js";import"./chunk-MAIHBBWT.js";import"./chunk-D65T4H6J.js";import{useState as l,useCallback as r,useEffect as i}from"react";import{jsxs as o,Fragment as a,jsx as c}from"react/jsx-runtime";var s="calc_appearance";function u(){const[u,d]=l("0"),[h,m]=l(null),[b,k]=l(null),[g,p]=l(!1),[x,v]=l(""),[N,y]=l([]),[f,C]=l(!1),[$,j]=l(()=>e(s)),[w,L]=l(!1),[E,B]=l($);n(r(()=>{B({...$}),L(!0)},[$]));const T=r(e=>{v(""),d(t=>g||"0"===t?(p(!1),"."===e?"0.":e):"."===e&&t.includes(".")?t:t+e)},[g]),W=r(()=>{d("0"),m(null),k(null),p(!1)},[]),F=r((e,t,n)=>{switch(n){case"+":return e+t;case"-":return e-t;case"×":return e*t;case"÷":return 0===t?0:e/t;default:return t}},[]),S=r(e=>{const t=parseFloat(u);if(null!==h&&b&&!g){const e=F(h,t,b);d(String(e)),m(e),y(n=>[`${h} ${b} ${t} = ${e}`,...n].slice(0,20))}else m(t);k(e),p(!0)},[u,h,b,g,F]),A=r(()=>{if(null===h||!b)return;const e=parseFloat(u),t=F(h,e,b),n=`${h} ${b} ${e} =`;y(e=>[`${n} ${t}`,...e].slice(0,20)),v(n),d(String(t)),m(null),k(null),p(!0)},[u,h,b,F]),I=r(()=>{d(e=>String(parseFloat(e)/100))},[]),J=r(()=>{d(e=>e.startsWith("-")?e.slice(1):"0"===e?e:"-"+e)},[]);i(()=>{const e=e=>{const t=e.target.tagName;"INPUT"!==t&&"TEXTAREA"!==t&&"SELECT"!==t&&(e.key>="0"&&e.key<="9"?T(e.key):"."===e.key?T("."):"+"===e.key?S("+"):"-"===e.key?S("-"):"*"===e.key?S("×"):"/"===e.key?(e.preventDefault(),S("÷")):"Enter"===e.key||"="===e.key?A():"Escape"===e.key?W():"Backspace"===e.key?d(e=>e.length>1?e.slice(0,-1):"0"):"%"===e.key&&I())};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[T,S,A,W,I]);const H="flex items-center justify-center rounded-lg text-sm font-medium transition-colors",M=`${H} bg-white border border-gray-200 text-gray-900 hover:bg-gray-50 active:bg-gray-100`,O=`${H} bg-blue-50 border border-blue-200 text-blue-700 hover:bg-blue-100 active:bg-blue-200`,U=`${H} bg-gray-100 border border-gray-200 text-gray-600 hover:bg-gray-200 active:bg-gray-300`,D=`${H} bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800`;/* @__PURE__ */
1
+ import{loadAppearance as e,WidgetSettingsModal as t}from"./chunk-NUPXFPQW.js";import{useWidgetSettings as n}from"./chunk-ZR72F7TE.js";import"./chunk-V453E4KU.js";import"./chunk-UB4JIOLD.js";import"./chunk-PH3PRIFP.js";import"./chunk-JNL7A5HC.js";import"./chunk-D4HWQ3ET.js";import"./chunk-57MIENLJ.js";import"./chunk-MAIHBBWT.js";import"./chunk-TR6PLN7W.js";import"./chunk-D65T4H6J.js";import{useState as l,useCallback as r,useEffect as i}from"react";import{jsxs as o,Fragment as a,jsx as c}from"react/jsx-runtime";var s="calc_appearance";function u(){const[u,d]=l("0"),[h,m]=l(null),[b,k]=l(null),[g,p]=l(!1),[x,N]=l(""),[v,y]=l([]),[f,C]=l(!1),[$,j]=l(()=>e(s)),[w,E]=l(!1),[L,T]=l($);n(r(()=>{T({...$}),E(!0)},[$]));const W=r(e=>{N(""),d(t=>g||"0"===t?(p(!1),"."===e?"0.":e):"."===e&&t.includes(".")?t:t+e)},[g]),B=r(()=>{d("0"),m(null),k(null),p(!1)},[]),F=r((e,t,n)=>{switch(n){case"+":return e+t;case"-":return e-t;case"×":return e*t;case"÷":return 0===t?0:e/t;default:return t}},[]),S=r(e=>{const t=parseFloat(u);if(null!==h&&b&&!g){const e=F(h,t,b);d(String(e)),m(e),y(n=>[`${h} ${b} ${t} = ${e}`,...n].slice(0,20))}else m(t);k(e),p(!0)},[u,h,b,g,F]),P=r(()=>{if(null===h||!b)return;const e=parseFloat(u),t=F(h,e,b),n=`${h} ${b} ${e} =`;y(e=>[`${n} ${t}`,...e].slice(0,20)),N(n),d(String(t)),m(null),k(null),p(!0)},[u,h,b,F]),A=r(()=>{d(e=>String(parseFloat(e)/100))},[]),H=r(()=>{d(e=>e.startsWith("-")?e.slice(1):"0"===e?e:"-"+e)},[]);i(()=>{const e=e=>{const t=e.target.tagName;"INPUT"!==t&&"TEXTAREA"!==t&&"SELECT"!==t&&(e.key>="0"&&e.key<="9"?W(e.key):"."===e.key?W("."):"+"===e.key?S("+"):"-"===e.key?S("-"):"*"===e.key?S("×"):"/"===e.key?(e.preventDefault(),S("÷")):"Enter"===e.key||"="===e.key?P():"Escape"===e.key?B():"Backspace"===e.key?d(e=>e.length>1?e.slice(0,-1):"0"):"%"===e.key&&A())};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[W,S,P,B,A]);const I="flex items-center justify-center rounded-lg text-sm font-medium transition-colors",J=`${I} bg-white border border-gray-200 text-gray-900 hover:bg-gray-50 active:bg-gray-100`,M=`${I} bg-blue-50 border border-blue-200 text-blue-700 hover:bg-blue-100 active:bg-blue-200`,D=`${I} bg-gray-100 border border-gray-200 text-gray-600 hover:bg-gray-200 active:bg-gray-300`,R=`${I} bg-blue-600 text-white hover:bg-blue-700 active:bg-blue-800`;/* @__PURE__ */
2
2
  return o(a,{children:[
3
3
  /* @__PURE__ */o("div",{className:"flex flex-col h-full",style:{opacity:$.activeOpacity/100,backdropFilter:$.activeBlur>0?`blur(${$.activeBlur}px)`:void 0},children:[
4
4
  /* @__PURE__ */o("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",children:[
5
- /* @__PURE__ */c("div",{className:"flex flex-col justify-end flex-1 overflow-hidden",children:N.slice(0,3).reverse().map((e,t)=>/* @__PURE__ */c("div",{className:"text-[11px] text-slate-400 text-right font-mono truncate",children:e},t))}),(x||b&&null!==h)&&/* @__PURE__ */c("div",{className:"text-base text-slate-600 text-right font-mono",children:x||`${h} ${b} ${g?"":u}`}),
5
+ /* @__PURE__ */c("div",{className:"flex flex-col justify-end flex-1 overflow-hidden",children:v.slice(0,3).reverse().map((e,t)=>/* @__PURE__ */c("div",{className:"text-[11px] text-slate-400 text-right font-mono truncate",children:e},t))}),(x||b&&null!==h)&&/* @__PURE__ */c("div",{className:"text-base text-slate-600 text-right font-mono",children:x||`${h} ${b} ${g?"":u}`}),
6
6
  /* @__PURE__ */o("div",{className:"flex items-center gap-2",children:[
7
7
  /* @__PURE__ */c("button",{onClick:()=>{navigator.clipboard.writeText(u),C(!0),setTimeout(()=>C(!1),1200)},title:"Copy result",className:"shrink-0 text-slate-300 hover:text-slate-500 transition-colors opacity-0 group-hover/display:opacity-100",children:/* @__PURE__ */c("svg",f?{className:"h-5 w-5 text-green-500",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:/* @__PURE__ */c("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 13l4 4L19 7"})}:{className:"h-5 w-5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:2,children:/* @__PURE__ */c("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"})})}),
8
8
  /* @__PURE__ */c("div",{className:"text-right text-4xl font-mono font-bold text-slate-800 truncate flex-1",children:(e=>{const t=parseFloat(e);return isNaN(t)||e.endsWith(".")||e.endsWith(".0")||Math.abs(t)<1e15&&e.length<16?e:t.toExponential(6)})(u)})]})]}),
9
9
  /* @__PURE__ */o("div",{className:"grid grid-cols-4 grid-rows-5 gap-1.5 flex-1 min-h-0 p-2",children:[
10
- /* @__PURE__ */c("button",{onClick:W,className:U,children:"AC"}),
11
- /* @__PURE__ */c("button",{onClick:J,className:U,children:"+/−"}),
12
- /* @__PURE__ */c("button",{onClick:I,className:U,children:"%"}),
13
- /* @__PURE__ */c("button",{onClick:()=>S("÷"),className:`${O} ${"÷"===b?"ring-2 ring-blue-400":""}`,children:"÷"}),
14
- /* @__PURE__ */c("button",{onClick:()=>T("7"),className:M,children:"7"}),
15
- /* @__PURE__ */c("button",{onClick:()=>T("8"),className:M,children:"8"}),
16
- /* @__PURE__ */c("button",{onClick:()=>T("9"),className:M,children:"9"}),
17
- /* @__PURE__ */c("button",{onClick:()=>S("×"),className:`${O} ${"×"===b?"ring-2 ring-blue-400":""}`,children:"×"}),
18
- /* @__PURE__ */c("button",{onClick:()=>T("4"),className:M,children:"4"}),
19
- /* @__PURE__ */c("button",{onClick:()=>T("5"),className:M,children:"5"}),
20
- /* @__PURE__ */c("button",{onClick:()=>T("6"),className:M,children:"6"}),
21
- /* @__PURE__ */c("button",{onClick:()=>S("-"),className:`${O} ${"-"===b?"ring-2 ring-blue-400":""}`,children:"−"}),
22
- /* @__PURE__ */c("button",{onClick:()=>T("1"),className:M,children:"1"}),
23
- /* @__PURE__ */c("button",{onClick:()=>T("2"),className:M,children:"2"}),
24
- /* @__PURE__ */c("button",{onClick:()=>T("3"),className:M,children:"3"}),
25
- /* @__PURE__ */c("button",{onClick:()=>S("+"),className:`${O} ${"+"===b?"ring-2 ring-blue-400":""}`,children:"+"}),
26
- /* @__PURE__ */c("button",{onClick:()=>T("0"),className:`${M} col-span-2`,children:"0"}),
27
- /* @__PURE__ */c("button",{onClick:()=>T("."),className:M,children:"."}),
28
- /* @__PURE__ */c("button",{onClick:A,className:D,children:"="})]})]}),
29
- /* @__PURE__ */c(t,{open:w,onClose:()=>L(!1),title:"Calculator Settings",appearance:E,onAppearanceChange:B,onSave:()=>{j(E),localStorage.setItem(s,JSON.stringify(E)),L(!1)}})]})}export{u as default};
10
+ /* @__PURE__ */c("button",{onClick:B,className:D,children:"AC"}),
11
+ /* @__PURE__ */c("button",{onClick:H,className:D,children:"+/−"}),
12
+ /* @__PURE__ */c("button",{onClick:A,className:D,children:"%"}),
13
+ /* @__PURE__ */c("button",{onClick:()=>S("÷"),className:`${M} ${"÷"===b?"ring-2 ring-blue-400":""}`,children:"÷"}),
14
+ /* @__PURE__ */c("button",{onClick:()=>W("7"),className:J,children:"7"}),
15
+ /* @__PURE__ */c("button",{onClick:()=>W("8"),className:J,children:"8"}),
16
+ /* @__PURE__ */c("button",{onClick:()=>W("9"),className:J,children:"9"}),
17
+ /* @__PURE__ */c("button",{onClick:()=>S("×"),className:`${M} ${"×"===b?"ring-2 ring-blue-400":""}`,children:"×"}),
18
+ /* @__PURE__ */c("button",{onClick:()=>W("4"),className:J,children:"4"}),
19
+ /* @__PURE__ */c("button",{onClick:()=>W("5"),className:J,children:"5"}),
20
+ /* @__PURE__ */c("button",{onClick:()=>W("6"),className:J,children:"6"}),
21
+ /* @__PURE__ */c("button",{onClick:()=>S("-"),className:`${M} ${"-"===b?"ring-2 ring-blue-400":""}`,children:"−"}),
22
+ /* @__PURE__ */c("button",{onClick:()=>W("1"),className:J,children:"1"}),
23
+ /* @__PURE__ */c("button",{onClick:()=>W("2"),className:J,children:"2"}),
24
+ /* @__PURE__ */c("button",{onClick:()=>W("3"),className:J,children:"3"}),
25
+ /* @__PURE__ */c("button",{onClick:()=>S("+"),className:`${M} ${"+"===b?"ring-2 ring-blue-400":""}`,children:"+"}),
26
+ /* @__PURE__ */c("button",{onClick:()=>W("0"),className:`${J} col-span-2`,children:"0"}),
27
+ /* @__PURE__ */c("button",{onClick:()=>W("."),className:J,children:"."}),
28
+ /* @__PURE__ */c("button",{onClick:P,className:R,children:"="})]})]}),
29
+ /* @__PURE__ */c(t,{open:w,onClose:()=>E(!1),title:"Calculator Settings",appearance:L,onAppearanceChange:T,onSave:()=>{j(L),localStorage.setItem(s,JSON.stringify(L)),E(!1)}})]})}export{u as default};
@@ -1 +1 @@
1
- export{ConfirmProvider,confirm,confirmDestructive,prompt,useConfirm}from"./chunk-3A46NCXK.js";import"./chunk-OQDWLHZI.js";
1
+ export{ConfirmProvider,confirm,confirmDestructive,prompt,useConfirm}from"./chunk-D4HWQ3ET.js";import"./chunk-TR6PLN7W.js";
@@ -1,4 +1,4 @@
1
- import{loadAppearance as e,WidgetSettingsModal as t}from"./chunk-H562R4GX.js";import{useWidgetSettings as r}from"./chunk-JN4WXTWJ.js";import"./chunk-UB4JIOLD.js";import"./chunk-44UO7FYU.js";import"./chunk-PH3PRIFP.js";import"./chunk-UZAJ5BF6.js";import"./chunk-3A46NCXK.js";import"./chunk-57MIENLJ.js";import"./chunk-OQDWLHZI.js";import"./chunk-MAIHBBWT.js";import"./chunk-D65T4H6J.js";import{useState as a,useCallback as n,useEffect as s}from"react";import{jsxs as c,Fragment as o,jsx as i}from"react/jsx-runtime";var l=["USD","CNY","AUD","GBP","EUR","JPY","CAD","THB","NZD","SGD","HKD","CHF","KRW","INR","MXN","BRL"],m=[["USD","CNY"],["USD","AUD"],["GBP","USD"],["USD","JPY"]],d="currency_pairs",u="currency_appearance",p="currency_rates_cache";function h(e){try{const t=JSON.parse(localStorage.getItem(p)||"{}")[e];if(t&&Date.now()-t.timestamp<36e5)return t.rates}catch{}return null}function g(e,t){try{const r=JSON.parse(localStorage.getItem(p)||"{}");r[e]={rates:t,timestamp:Date.now()},localStorage.setItem(p,JSON.stringify(r))}catch{}}function x(){try{const e=JSON.parse(localStorage.getItem(d)||"");if(Array.isArray(e)&&e.length)return e.slice(0,4)}catch{}return m}var y={USD:"🇺🇸",CNY:"🇨🇳",AUD:"🇦🇺",GBP:"🇬🇧",JPY:"🇯🇵",CAD:"🇨🇦",EUR:"🇪🇺",THB:"🇹🇭",NZD:"🇳🇿",SGD:"🇸🇬",HKD:"🇭🇰",CHF:"🇨🇭",KRW:"🇰🇷",INR:"🇮🇳",MXN:"🇲🇽",BRL:"🇧🇷"};function f(){const[m,p]=a(x),[f,N]=a(()=>e(u)),[b,v]=a({}),[S,D]=a(!1),[k,C]=a(!1),[j,J]=a([]),[U,B]=a(f),[I,A]=a("USD"),[H,O]=a("CNY");r(n(()=>{J([...m]),B({...f}),C(!0)},[m,f])),s(()=>{const e=[...new Set(m.map(([e])=>e))];let t=!0;return async function(){D(!0);const r={};for(const t of e){const e=h(t);if(e)r[t]=e;else try{const e=await fetch(`https://open.er-api.com/v6/latest/${t}`),a=await e.json();a.rates&&(r[t]=a.rates,g(t,a.rates))}catch{}}t&&(v(r),D(!1))}(),()=>{t=!1}},[m]);/* @__PURE__ */
1
+ import{loadAppearance as e,WidgetSettingsModal as t}from"./chunk-NUPXFPQW.js";import{useWidgetSettings as r}from"./chunk-ZR72F7TE.js";import"./chunk-V453E4KU.js";import"./chunk-UB4JIOLD.js";import"./chunk-PH3PRIFP.js";import"./chunk-JNL7A5HC.js";import"./chunk-D4HWQ3ET.js";import"./chunk-57MIENLJ.js";import"./chunk-MAIHBBWT.js";import"./chunk-TR6PLN7W.js";import"./chunk-D65T4H6J.js";import{useState as a,useCallback as n,useEffect as s}from"react";import{jsxs as c,Fragment as o,jsx as i}from"react/jsx-runtime";var l=["USD","CNY","AUD","GBP","EUR","JPY","CAD","THB","NZD","SGD","HKD","CHF","KRW","INR","MXN","BRL"],m=[["USD","CNY"],["USD","AUD"],["GBP","USD"],["USD","JPY"]],d="currency_pairs",u="currency_appearance",p="currency_rates_cache";function h(e){try{const t=JSON.parse(localStorage.getItem(p)||"{}")[e];if(t&&Date.now()-t.timestamp<36e5)return t.rates}catch{}return null}function g(e,t){try{const r=JSON.parse(localStorage.getItem(p)||"{}");r[e]={rates:t,timestamp:Date.now()},localStorage.setItem(p,JSON.stringify(r))}catch{}}function x(){try{const e=JSON.parse(localStorage.getItem(d)||"");if(Array.isArray(e)&&e.length)return e.slice(0,4)}catch{}return m}var y={USD:"🇺🇸",CNY:"🇨🇳",AUD:"🇦🇺",GBP:"🇬🇧",JPY:"🇯🇵",CAD:"🇨🇦",EUR:"🇪🇺",THB:"🇹🇭",NZD:"🇳🇿",SGD:"🇸🇬",HKD:"🇭🇰",CHF:"🇨🇭",KRW:"🇰🇷",INR:"🇮🇳",MXN:"🇲🇽",BRL:"🇧🇷"};function f(){const[m,p]=a(x),[f,N]=a(()=>e(u)),[b,v]=a({}),[S,D]=a(!1),[k,C]=a(!1),[j,U]=a([]),[J,B]=a(f),[I,P]=a("USD"),[A,H]=a("CNY");r(n(()=>{U([...m]),B({...f}),C(!0)},[m,f])),s(()=>{const e=[...new Set(m.map(([e])=>e))];let t=!0;return async function(){D(!0);const r={};for(const t of e){const e=h(t);if(e)r[t]=e;else try{const e=await fetch(`https://open.er-api.com/v6/latest/${t}`),a=await e.json();a.rates&&(r[t]=a.rates,g(t,a.rates))}catch{}}t&&(v(r),D(!1))}(),()=>{t=!1}},[m]);/* @__PURE__ */
2
2
  return c(o,{children:[
3
3
  /* @__PURE__ */i("div",{style:{backgroundColor:`rgb(var(--taskbar-bg-rgb, 243 244 246) / ${f.activeOpacity/100})`,backdropFilter:f.activeBlur>0?`blur(${f.activeBlur}px)`:void 0},children:/* @__PURE__ */c("div",{className:"px-4 py-3 space-y-1",children:[S&&/* @__PURE__ */i("div",{className:"text-xs text-gray-400 text-center py-4",children:"Loading rates..."}),m.map(([e,t],r)=>{const a=b[e]?.[t];/* @__PURE__ */
4
4
  return c("button",{onClick:()=>{const e=m.map((e,t)=>t===r?[e[1],e[0]]:e);p(e),localStorage.setItem(d,JSON.stringify(e))},className:"flex items-center justify-between px-2 py-2 border-b border-gray-200 last:border-0 w-full hover:bg-gray-100 rounded transition-colors cursor-pointer",children:[
@@ -9,13 +9,13 @@ return c("button",{onClick:()=>{const e=m.map((e,t)=>t===r?[e[1],e[0]]:e);p(e),l
9
9
  /* @__PURE__ */i("span",{className:"text-base",children:y[t]||""}),
10
10
  /* @__PURE__ */i("span",{className:"text-sm font-semibold text-gray-700",children:t})]}),
11
11
  /* @__PURE__ */i("div",{className:"text-base font-mono font-semibold text-gray-900 tabular-nums",children:null!=a?a.toLocaleString(void 0,{minimumFractionDigits:2,maximumFractionDigits:4}):"—"})]},`${e}-${t}-${r}`)})]})}),
12
- /* @__PURE__ */i(t,{open:k,onClose:()=>C(!1),title:"Currency Settings",appearance:U,onAppearanceChange:B,onSave:()=>{if(0===j.length)return;const e=j.slice(0,4);p(e),N(U),localStorage.setItem(d,JSON.stringify(e)),localStorage.setItem(u,JSON.stringify(U)),C(!1)},children:/* @__PURE__ */c("div",{children:[
12
+ /* @__PURE__ */i(t,{open:k,onClose:()=>C(!1),title:"Currency Settings",appearance:J,onAppearanceChange:B,onSave:()=>{if(0===j.length)return;const e=j.slice(0,4);p(e),N(J),localStorage.setItem(d,JSON.stringify(e)),localStorage.setItem(u,JSON.stringify(J)),C(!1)},children:/* @__PURE__ */c("div",{children:[
13
13
  /* @__PURE__ */c("h3",{className:"text-sm font-semibold text-gray-700 mb-2",children:["Currency Pairs ",
14
14
  /* @__PURE__ */c("span",{className:"text-[11px] font-normal text-gray-400",children:["(",j.length,"/",4,")"]})]}),
15
15
  /* @__PURE__ */i("div",{className:"space-y-1 mb-2",children:j.map(([e,t],r)=>/* @__PURE__ */c("div",{className:"flex items-center justify-between py-1 px-2 bg-gray-50 rounded",children:[
16
16
  /* @__PURE__ */c("span",{className:"text-sm",children:[y[e]||""," ",e," → ",y[t]||""," ",t]}),
17
- /* @__PURE__ */i("button",{onClick:()=>J(e=>e.filter((e,t)=>t!==r)),className:"text-red-400 hover:text-red-600",children:"×"})]},r))}),j.length<4?/* @__PURE__ */c("div",{className:"flex items-center gap-2",children:[
18
- /* @__PURE__ */i("select",{value:I,onChange:e=>A(e.target.value),className:"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",children:l.map(e=>/* @__PURE__ */i("option",{children:e},e))}),
17
+ /* @__PURE__ */i("button",{onClick:()=>U(e=>e.filter((e,t)=>t!==r)),className:"text-red-400 hover:text-red-600",children:"×"})]},r))}),j.length<4?/* @__PURE__ */c("div",{className:"flex items-center gap-2",children:[
18
+ /* @__PURE__ */i("select",{value:I,onChange:e=>P(e.target.value),className:"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",children:l.map(e=>/* @__PURE__ */i("option",{children:e},e))}),
19
19
  /* @__PURE__ */i("span",{className:"text-gray-400",children:"→"}),
20
- /* @__PURE__ */i("select",{value:H,onChange:e=>O(e.target.value),className:"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",children:l.map(e=>/* @__PURE__ */i("option",{children:e},e))}),
21
- /* @__PURE__ */i("button",{onClick:()=>{I!==H&&j.length<4&&J(e=>[...e,[I,H]])},className:"text-sm font-medium text-blue-600 hover:text-blue-800 px-2",children:"+ Add"})]}):/* @__PURE__ */c("p",{className:"text-[11px] text-gray-400 italic",children:["Max ",4," pairs — remove one to add another."]})]})})]})}export{f as default};
20
+ /* @__PURE__ */i("select",{value:A,onChange:e=>H(e.target.value),className:"bg-gray-50 border border-gray-200 rounded px-2 py-1 text-sm focus:outline-none focus:ring-1 focus:ring-blue-500",children:l.map(e=>/* @__PURE__ */i("option",{children:e},e))}),
21
+ /* @__PURE__ */i("button",{onClick:()=>{I!==A&&j.length<4&&U(e=>[...e,[I,A]])},className:"text-sm font-medium text-blue-600 hover:text-blue-800 px-2",children:"+ Add"})]}):/* @__PURE__ */c("p",{className:"text-[11px] text-gray-400 italic",children:["Max ",4," pairs — remove one to add another."]})]})})]})}export{f as default};
@@ -1,14 +1,14 @@
1
- import{AboutApp as e}from"./chunk-TAWKWSPN.js";import{WindowTitle as t}from"./chunk-JN4WXTWJ.js";import"./chunk-UB4JIOLD.js";import{escapeHtml as n}from"./chunk-BR22X7E5.js";import"./chunk-U4GTZPXP.js";import{toast_default as r}from"./chunk-VEGMDXT4.js";import"./chunk-44UO7FYU.js";import"./chunk-PH3PRIFP.js";import{PopupMenu as o,PopupMenuLabel as l,PopupMenuItem as a,PopupMenuDivider as i}from"./chunk-UZAJ5BF6.js";import"./chunk-3A46NCXK.js";import"./chunk-57MIENLJ.js";import"./chunk-OQDWLHZI.js";import"./chunk-MAIHBBWT.js";import"./chunk-D65T4H6J.js";import{useState as s,useRef as c}from"react";import{jsxs as d,jsx as h}from"react/jsx-runtime";var m=/* @__PURE__ */new Set(["txt","md","markdown","csv","tsv","log","json","xml","html","htm","css","js","jsx","ts","tsx","py","rb","go","rs","java","c","h","cpp","hpp","sh","bash","yml","yaml","toml","ini","conf","env","sql"]),u=/* @__PURE__ */new Set(["docx"]),p={filename:"Untitled",kind:"docx",content:"",ext:"docx"};function g(){const[g,x]=s(p),[f,y]=s(!1),[v,b]=s(!1),[k,w]=s(!1),[N,C]=s(null),D=c(null),j=c(null),L=c(null),M=(e,t)=>{L.current?.focus(),document.execCommand(e,!1,t),w(!0)},U=e=>{for(const t of e){if(!t.type.startsWith("image/"))continue;const e=new FileReader;e.onload=()=>{L.current?.focus(),document.execCommand("insertImage",!1,String(e.result)),w(!0)},e.readAsDataURL(t)}},B=e=>{N&&(N.el.removeAttribute("width"),N.el.removeAttribute("height"),N.el.style.width=e??"",N.el.style.height=e?"auto":"",w(!0),C(null))},W=async e=>{const t=(e.name.split(".").pop()||"").toLowerCase();y(!0);try{if(m.has(t)||!u.has(t)&&e.type.startsWith("text/")){const r=await e.text(),o=n(r).replace(/\n/g,"<br>");x({filename:e.name,kind:"text",content:o,ext:t}),w(!1)}else if(u.has(t))try{const n=await(import(
1
+ import{escapeHtml as e}from"./chunk-BR22X7E5.js";import{AboutApp as t}from"./chunk-NV35RTZK.js";import{toast_default as n}from"./chunk-KKOHUQPF.js";import"./chunk-6JXLEPRE.js";import{WindowTitle as r}from"./chunk-ZR72F7TE.js";import"./chunk-V453E4KU.js";import"./chunk-UB4JIOLD.js";import"./chunk-PH3PRIFP.js";import{PopupMenu as o,PopupMenuLabel as l,PopupMenuItem as a,PopupMenuDivider as i}from"./chunk-JNL7A5HC.js";import"./chunk-D4HWQ3ET.js";import"./chunk-57MIENLJ.js";import"./chunk-MAIHBBWT.js";import"./chunk-TR6PLN7W.js";import"./chunk-D65T4H6J.js";import{useState as s,useRef as c}from"react";import{jsxs as d,jsx as h}from"react/jsx-runtime";var m=/* @__PURE__ */new Set(["txt","md","markdown","csv","tsv","log","json","xml","html","htm","css","js","jsx","ts","tsx","py","rb","go","rs","java","c","h","cpp","hpp","sh","bash","yml","yaml","toml","ini","conf","env","sql"]),u=/* @__PURE__ */new Set(["docx"]),p={filename:"Untitled",kind:"docx",content:"",ext:"docx"};function g(){const[g,x]=s(p),[f,y]=s(!1),[v,b]=s(!1),[k,w]=s(!1),[N,C]=s(null),L=c(null),j=c(null),D=c(null),M=(e,t)=>{D.current?.focus(),document.execCommand(e,!1,t),w(!0)},R=e=>{for(const t of e){if(!t.type.startsWith("image/"))continue;const e=new FileReader;e.onload=()=>{D.current?.focus(),document.execCommand("insertImage",!1,String(e.result)),w(!0)},e.readAsDataURL(t)}},U=e=>{N&&(N.el.removeAttribute("width"),N.el.removeAttribute("height"),N.el.style.width=e??"",N.el.style.height=e?"auto":"",w(!0),C(null))},B=async t=>{const r=(t.name.split(".").pop()||"").toLowerCase();y(!0);try{if(m.has(r)||!u.has(r)&&t.type.startsWith("text/")){const n=await t.text(),o=e(n).replace(/\n/g,"<br>");x({filename:t.name,kind:"text",content:o,ext:r}),w(!1)}else if(u.has(r))try{const e=await(import(
2
2
  /* @vite-ignore */
3
- "mammoth")),r=await e.arrayBuffer(),o=await n.convertToHtml({arrayBuffer:r});x({filename:e.name,kind:"docx",content:o.value||"<p><em>(empty document)</em></p>",ext:t}),w(!1)}catch(e){r.error('Install the optional "mammoth" peer dep to read .docx files.')}else"doc"===t?r.error(".doc is the legacy Word binary format — convert to .docx first."):r.error(`Unsupported file type: .${t||"unknown"}`)}finally{y(!1)}},R=g?.filename?(A=g.filename).length>24?`${A.slice(0,23)}…`:A:"Untitled";var A;/* @__PURE__ */
4
- return d("div",{className:"relative flex flex-col h-full",onDragOver:e=>{e.preventDefault(),v||b(!0)},onDragLeave:e=>{e.currentTarget===e.target&&b(!1)},onDrop:e=>{e.preventDefault(),b(!1);const t=Array.from(e.dataTransfer.files??[]);0!==t.length&&(t.every(e=>e.type.startsWith("image/"))?U(t):W(t[0]))},children:[
5
- /* @__PURE__ */h(t,{title:`${R}${k?" •":""} - Documents`}),
6
- /* @__PURE__ */h(e,{app:"documents"}),
3
+ "mammoth")),n=await t.arrayBuffer(),o=await e.convertToHtml({arrayBuffer:n});x({filename:t.name,kind:"docx",content:o.value||"<p><em>(empty document)</em></p>",ext:r}),w(!1)}catch(e){n.error('Install the optional "mammoth" peer dep to read .docx files.')}else"doc"===r?n.error(".doc is the legacy Word binary format — convert to .docx first."):n.error(`Unsupported file type: .${r||"unknown"}`)}finally{y(!1)}},T=g?.filename?(W=g.filename).length>24?`${W.slice(0,23)}…`:W:"Untitled";var W;/* @__PURE__ */
4
+ return d("div",{className:"relative flex flex-col h-full",onDragOver:e=>{e.preventDefault(),v||b(!0)},onDragLeave:e=>{e.currentTarget===e.target&&b(!1)},onDrop:e=>{e.preventDefault(),b(!1);const t=Array.from(e.dataTransfer.files??[]);0!==t.length&&(t.every(e=>e.type.startsWith("image/"))?R(t):B(t[0]))},children:[
5
+ /* @__PURE__ */h(r,{title:`${T}${k?" •":""} - Documents`}),
6
+ /* @__PURE__ */h(t,{app:"documents"}),
7
7
  /* @__PURE__ */d("div",{className:"flex items-center flex-wrap gap-x-2 gap-y-1 px-3 py-2 border-b border-gray-200 bg-gray-50 shrink-0",children:[
8
- /* @__PURE__ */h("input",{ref:D,type:"file",accept:".txt,.md,.csv,.tsv,.log,.json,.xml,.html,.htm,.css,.js,.jsx,.ts,.tsx,.py,.rb,.go,.rs,.java,.c,.h,.cpp,.hpp,.sh,.bash,.yml,.yaml,.toml,.ini,.conf,.env,.sql,.docx,text/*",onChange:e=>{const t=e.target.files?.[0];t&&W(t),D.current&&(D.current.value="")},className:"hidden"}),
9
- /* @__PURE__ */d("button",{onClick:()=>D.current?.click(),className:"text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors flex items-center gap-1",children:[
8
+ /* @__PURE__ */h("input",{ref:L,type:"file",accept:".txt,.md,.csv,.tsv,.log,.json,.xml,.html,.htm,.css,.js,.jsx,.ts,.tsx,.py,.rb,.go,.rs,.java,.c,.h,.cpp,.hpp,.sh,.bash,.yml,.yaml,.toml,.ini,.conf,.env,.sql,.docx,text/*",onChange:e=>{const t=e.target.files?.[0];t&&B(t),L.current&&(L.current.value="")},className:"hidden"}),
9
+ /* @__PURE__ */d("button",{onClick:()=>L.current?.click(),className:"text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors flex items-center gap-1",children:[
10
10
  /* @__PURE__ */h("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */h("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M3.75 9.776c.112-.017.227-.026.344-.026h15.812c.117 0 .232.009.344.026m-16.5 0a2.25 2.25 0 00-1.883 2.542l.857 6a2.25 2.25 0 002.227 1.932H19.05a2.25 2.25 0 002.227-1.932l.857-6a2.25 2.25 0 00-1.883-2.542m-16.5 0V6A2.25 2.25 0 016 3.75h3.879a1.5 1.5 0 011.06.44l2.122 2.12a1.5 1.5 0 001.06.44H18A2.25 2.25 0 0120.25 9v.776"})}),"Open"]}),
11
- /* @__PURE__ */h("button",{onClick:()=>{if(!g)return;const e=L.current?.innerHTML??"";if("text"===g.kind){if(/<(b|strong|i|em|u|font|span style|ul|ol|img)|style="[^"]*text-align/i.test(e)){const t=new Blob([`<!doctype html><meta charset="utf-8"><title>${n(g.filename)}</title><body style="font-family:ui-monospace,monospace;white-space:pre-wrap">${e}`],{type:"text/html"}),r=URL.createObjectURL(t),o=document.createElement("a");o.href=r,o.download=`${g.filename.replace(/\.[^.]+$/,"")}.html`,o.click(),URL.revokeObjectURL(r)}else{const e=L.current?.innerText??"",t=new Blob([e],{type:"text/plain"}),n=URL.createObjectURL(t),r=document.createElement("a");r.href=n,r.download=g.filename,r.click(),URL.revokeObjectURL(n)}}else{const t=new Blob([`<!doctype html><meta charset="utf-8"><title>${n(g.filename)}</title>${e}`],{type:"text/html"}),r=URL.createObjectURL(t),o=document.createElement("a");o.href=r,o.download=`${g.filename.replace(/\.docx$/i,"")}.html`,o.click(),URL.revokeObjectURL(r)}},className:"text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors",children:"Save"}),
11
+ /* @__PURE__ */h("button",{onClick:()=>{if(!g)return;const t=D.current?.innerHTML??"";if("text"===g.kind){if(/<(b|strong|i|em|u|font|span style|ul|ol|img)|style="[^"]*text-align/i.test(t)){const n=new Blob([`<!doctype html><meta charset="utf-8"><title>${e(g.filename)}</title><body style="font-family:ui-monospace,monospace;white-space:pre-wrap">${t}`],{type:"text/html"}),r=URL.createObjectURL(n),o=document.createElement("a");o.href=r,o.download=`${g.filename.replace(/\.[^.]+$/,"")}.html`,o.click(),URL.revokeObjectURL(r)}else{const e=D.current?.innerText??"",t=new Blob([e],{type:"text/plain"}),n=URL.createObjectURL(t),r=document.createElement("a");r.href=n,r.download=g.filename,r.click(),URL.revokeObjectURL(n)}}else{const n=new Blob([`<!doctype html><meta charset="utf-8"><title>${e(g.filename)}</title>${t}`],{type:"text/html"}),r=URL.createObjectURL(n),o=document.createElement("a");o.href=r,o.download=`${g.filename.replace(/\.docx$/i,"")}.html`,o.click(),URL.revokeObjectURL(r)}},className:"text-xs text-gray-700 hover:text-gray-900 px-2 py-1 rounded hover:bg-gray-200 transition-colors",children:"Save"}),
12
12
  /* @__PURE__ */h("span",{className:"text-[10px] text-gray-400 ml-1",children:"TXT · DOCX · Code"}),
13
13
  /* @__PURE__ */h("div",{className:"h-4 w-px bg-gray-300 mx-1"}),
14
14
  /* @__PURE__ */d("span",{className:"text-xs font-medium text-gray-700 truncate max-w-[200px]",title:g.filename,children:[g.filename,k?" •":""]}),
@@ -38,15 +38,15 @@ return d("div",{className:"relative flex flex-col h-full",onDragOver:e=>{e.preve
38
38
  /* @__PURE__ */h("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>M("justifyRight"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Align right",children:/* @__PURE__ */h("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */h("path",{strokeLinecap:"round",d:"M3.75 6h16.5M10.5 12h9.75M6 18h14.25"})})}),
39
39
  /* @__PURE__ */h("button",{onMouseDown:e=>e.preventDefault(),onClick:()=>M("justifyFull"),className:"px-2 py-1 rounded text-gray-700 hover:bg-gray-200 transition-colors",title:"Justify",children:/* @__PURE__ */h("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.8,children:/* @__PURE__ */h("path",{strokeLinecap:"round",d:"M3.75 6h16.5M3.75 12h16.5M3.75 18h16.5"})})})]}),
40
40
  /* @__PURE__ */h("div",{className:"h-4 w-px bg-gray-300 mx-1"}),
41
- /* @__PURE__ */h("input",{ref:j,type:"file",accept:"image/*",multiple:!0,className:"hidden",onChange:e=>{e.target.files?.length&&U(Array.from(e.target.files)),j.current&&(j.current.value="")}}),
41
+ /* @__PURE__ */h("input",{ref:j,type:"file",accept:"image/*",multiple:!0,className:"hidden",onChange:e=>{e.target.files?.length&&R(Array.from(e.target.files)),j.current&&(j.current.value="")}}),
42
42
  /* @__PURE__ */d("button",{onClick:()=>j.current?.click(),className:"px-2 py-1 text-xs rounded text-gray-700 hover:bg-gray-200 transition-colors flex items-center gap-1",title:"Insert image (or paste / drop one)",children:[
43
43
  /* @__PURE__ */h("svg",{className:"h-3.5 w-3.5",fill:"none",viewBox:"0 0 24 24",stroke:"currentColor",strokeWidth:1.5,children:/* @__PURE__ */h("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M2.25 15.75l5.159-5.159a2.25 2.25 0 013.182 0l5.159 5.159m-1.5-1.5l1.409-1.409a2.25 2.25 0 013.182 0l2.909 2.909m-18 3.75h16.5a1.5 1.5 0 001.5-1.5V6a1.5 1.5 0 00-1.5-1.5H3.75A1.5 1.5 0 002.25 6v12a1.5 1.5 0 001.5 1.5zm10.5-11.25h.008v.008h-.008V8.25zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z"})}),"Image"]})]}),
44
- /* @__PURE__ */h("div",{className:"flex-1 min-h-0 overflow-hidden",children:/* @__PURE__ */h("div",f?{className:"flex items-center justify-center h-full text-sm text-gray-400",children:"Loading…"}:{className:"h-full overflow-auto "+("docx"===g.kind?"bg-gray-100 px-6 py-8":"bg-white"),children:/* @__PURE__ */h("div",{ref:L,contentEditable:!0,suppressContentEditableWarning:!0,spellCheck:!1,onInput:()=>w(!0),onPaste:e=>{const t=Array.from(e.clipboardData?.items??[]).filter(e=>"file"===e.kind&&e.type.startsWith("image/")).map(e=>e.getAsFile()).filter(e=>!!e);t.length&&(e.preventDefault(),U(t))},onClick:e=>{const t=e.target;"IMG"===t.tagName&&(e.preventDefault(),C({x:e.clientX,y:e.clientY,el:t}))},dangerouslySetInnerHTML:{__html:g.content},className:"docx"===g.kind?"docs-editor mx-auto w-[8.5in] min-h-[11in] bg-white p-[1in] shadow text-[14px] leading-relaxed text-gray-800 outline-none focus:ring-2 focus:ring-blue-400/40":"docs-editor block w-full min-h-full p-4 font-mono text-[13px] leading-relaxed text-gray-800 bg-white outline-none whitespace-pre-wrap"})})}),v&&/* @__PURE__ */h("div",{className:"absolute inset-0 bg-blue-500/15 border-4 border-dashed border-blue-500 pointer-events-none flex items-center justify-center z-20",children:/* @__PURE__ */h("div",{className:"px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium shadow-lg",children:"Drop a document to open · images insert into the page"})}),N&&/* @__PURE__ */d(o,{minWidth:150,style:{left:N.x,top:N.y},onClose:()=>C(null),children:[
44
+ /* @__PURE__ */h("div",{className:"flex-1 min-h-0 overflow-hidden",children:/* @__PURE__ */h("div",f?{className:"flex items-center justify-center h-full text-sm text-gray-400",children:"Loading…"}:{className:"h-full overflow-auto "+("docx"===g.kind?"bg-gray-100 px-6 py-8":"bg-white"),children:/* @__PURE__ */h("div",{ref:D,contentEditable:!0,suppressContentEditableWarning:!0,spellCheck:!1,onInput:()=>w(!0),onPaste:e=>{const t=Array.from(e.clipboardData?.items??[]).filter(e=>"file"===e.kind&&e.type.startsWith("image/")).map(e=>e.getAsFile()).filter(e=>!!e);t.length&&(e.preventDefault(),R(t))},onClick:e=>{const t=e.target;"IMG"===t.tagName&&(e.preventDefault(),C({x:e.clientX,y:e.clientY,el:t}))},dangerouslySetInnerHTML:{__html:g.content},className:"docx"===g.kind?"docs-editor mx-auto w-[8.5in] min-h-[11in] bg-white p-[1in] shadow text-[14px] leading-relaxed text-gray-800 outline-none focus:ring-2 focus:ring-blue-400/40":"docs-editor block w-full min-h-full p-4 font-mono text-[13px] leading-relaxed text-gray-800 bg-white outline-none whitespace-pre-wrap"})})}),v&&/* @__PURE__ */h("div",{className:"absolute inset-0 bg-blue-500/15 border-4 border-dashed border-blue-500 pointer-events-none flex items-center justify-center z-20",children:/* @__PURE__ */h("div",{className:"px-4 py-2 rounded-md bg-blue-600 text-white text-sm font-medium shadow-lg",children:"Drop a document to open · images insert into the page"})}),N&&/* @__PURE__ */d(o,{minWidth:150,style:{left:N.x,top:N.y},onClose:()=>C(null),children:[
45
45
  /* @__PURE__ */h(l,{children:"Image width"}),
46
- /* @__PURE__ */h(a,{onClick:()=>B("25%"),children:"25%"}),
47
- /* @__PURE__ */h(a,{onClick:()=>B("50%"),children:"50%"}),
48
- /* @__PURE__ */h(a,{onClick:()=>B("75%"),children:"75%"}),
49
- /* @__PURE__ */h(a,{onClick:()=>B("100%"),children:"100%"}),
50
- /* @__PURE__ */h(a,{onClick:()=>B(null),children:"Original size"}),
46
+ /* @__PURE__ */h(a,{onClick:()=>U("25%"),children:"25%"}),
47
+ /* @__PURE__ */h(a,{onClick:()=>U("50%"),children:"50%"}),
48
+ /* @__PURE__ */h(a,{onClick:()=>U("75%"),children:"75%"}),
49
+ /* @__PURE__ */h(a,{onClick:()=>U("100%"),children:"100%"}),
50
+ /* @__PURE__ */h(a,{onClick:()=>U(null),children:"Original size"}),
51
51
  /* @__PURE__ */h(i,{}),
52
52
  /* @__PURE__ */h(a,{danger:!0,onClick:()=>{N?.el.remove(),w(!0),C(null)},children:"Remove image"})]})]})}export{g as default};