@leef-l/macos-ui-kit 0.0.0-stage → 0.1.1

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 (45) hide show
  1. package/README.md +215 -2
  2. package/dist/assets/Inter Font License.txt +93 -0
  3. package/dist/assets/Inter-Medium.ttf +0 -0
  4. package/dist/component.d.ts +47 -0
  5. package/dist/controls/badge.d.ts +27 -0
  6. package/dist/controls/button.d.ts +41 -0
  7. package/dist/controls/checkbox.d.ts +40 -0
  8. package/dist/controls/combobox.d.ts +105 -0
  9. package/dist/controls/datepicker.d.ts +42 -0
  10. package/dist/controls/disclosure.d.ts +40 -0
  11. package/dist/controls/numberfield.d.ts +60 -0
  12. package/dist/controls/progress.d.ts +33 -0
  13. package/dist/controls/segmented.d.ts +64 -0
  14. package/dist/controls/select.d.ts +42 -0
  15. package/dist/controls/slider.d.ts +49 -0
  16. package/dist/controls/spinner.d.ts +24 -0
  17. package/dist/controls/splitview.d.ts +76 -0
  18. package/dist/controls/switch.d.ts +47 -0
  19. package/dist/controls/table.d.ts +103 -0
  20. package/dist/controls/textarea.d.ts +49 -0
  21. package/dist/controls/textfield.d.ts +63 -0
  22. package/dist/dom.d.ts +35 -0
  23. package/dist/index.d.ts +85 -0
  24. package/dist/index.js +2053 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/layout/sidebar.d.ts +47 -0
  27. package/dist/layout/statusbar.d.ts +30 -0
  28. package/dist/layout/tabview.d.ts +74 -0
  29. package/dist/layout/titlebar.d.ts +49 -0
  30. package/dist/layout/toolbar.d.ts +54 -0
  31. package/dist/layout/traffic-lights.d.ts +37 -0
  32. package/dist/macos-ui-kit.css +2 -0
  33. package/dist/macos-ui-kit.iife.js +2 -0
  34. package/dist/macos-ui-kit.iife.js.map +1 -0
  35. package/dist/mobile/shell.d.ts +59 -0
  36. package/dist/overlay.d.ts +41 -0
  37. package/dist/overlays/dialog.d.ts +46 -0
  38. package/dist/overlays/menu.d.ts +58 -0
  39. package/dist/overlays/popover.d.ts +90 -0
  40. package/dist/overlays/sheet.d.ts +56 -0
  41. package/dist/overlays/toast.d.ts +37 -0
  42. package/dist/overlays/tooltip.d.ts +48 -0
  43. package/dist/style.css.d.ts +2 -0
  44. package/dist/theme.d.ts +100 -0
  45. package/package.json +55 -4
@@ -0,0 +1,59 @@
1
+ import { Component } from "../component";
2
+ import "./shell.css";
3
+ export interface MobileTab {
4
+ /** Stable key. */
5
+ id: string;
6
+ /** Short label under the icon. */
7
+ label: string;
8
+ /** SVG path data for the tab icon. */
9
+ icon: string;
10
+ }
11
+ export interface MobileShellOptions {
12
+ /** Large navigation title shown above the content. */
13
+ title: string;
14
+ /** Tab bar entries. Empty hides the bar (a single-pane screen). */
15
+ tabs?: MobileTab[];
16
+ /** Initially visible tab. */
17
+ selected?: string;
18
+ /** Fired when the tab changes. */
19
+ onTabChange?: (id: string) => void;
20
+ /** Optional trailing action on the navigation bar. */
21
+ action?: HTMLElement;
22
+ }
23
+ /**
24
+ * The mobile counterpart of the desktop shell.
25
+ *
26
+ * Deliberately separate from `layout/TitleBar` rather than a media-query variant
27
+ * of it. A phone has no draggable window, no traffic lights and no persistent
28
+ * sidebar; navigation lives in a bottom tab bar, which is the platform-native
29
+ * pattern. Sharing markup between the two would mean every style rule carrying
30
+ * a device exception, which is harder to reason about than two focused modules.
31
+ *
32
+ * Panes are plain containers the host app fills, so this stays layout-only.
33
+ */
34
+ export declare class MobileShell extends Component<HTMLElement> {
35
+ protected readonly root: HTMLElement;
36
+ private readonly titleNode;
37
+ private readonly contentNode;
38
+ private readonly tabBar;
39
+ private readonly onTabChange?;
40
+ private currentId;
41
+ constructor(options: MobileShellOptions);
42
+ /** The region host panes are appended to. */
43
+ get content(): HTMLElement;
44
+ /** Update the large title. */
45
+ setTitle(title: string): void;
46
+ /** Switch the active tab. */
47
+ setSelected(id: string, notify?: boolean): void;
48
+ /** The active tab id, or null when the shell has no tabs. */
49
+ get selected(): string | null;
50
+ }
51
+ /**
52
+ * Detect whether this build is running inside a mobile Wails shell.
53
+ *
54
+ * Wails reports the host through `runtime.GOOS` on the Go side, but the
55
+ * frontend also needs an answer before any binding round-trip, so this checks
56
+ * the viewport instead: a coarse pointer plus a narrow window is a phone or
57
+ * tablet. The host app can override the decision either way.
58
+ */
59
+ export declare function looksLikeMobile(): boolean;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Overlay plumbing shared by Menu, Tooltip and Dialog.
3
+ *
4
+ * Positioning is the part every hand-written popover gets wrong: it clamps in
5
+ * one direction, forgets the scroll offset, or escapes the viewport when the
6
+ * anchor sits near an edge. This module resolves a placement against the
7
+ * viewport, flips when there is no room, and re-resolves on scroll and resize
8
+ * (via rAF so a burst of events costs one layout pass).
9
+ */
10
+ export type Placement = "top" | "top-start" | "top-end" | "bottom" | "bottom-start" | "bottom-end" | "left" | "right";
11
+ export interface PositionOptions {
12
+ /** Element the overlay points at. */
13
+ anchor: HTMLElement;
14
+ /** Overlay to place. Must already be in the document. */
15
+ panel: HTMLElement;
16
+ /** Preferred side; flips automatically when there is no room. */
17
+ placement?: Placement;
18
+ /** Gap between the anchor and the panel, in px. */
19
+ offset?: number;
20
+ /** Keep this much space from the viewport edges. */
21
+ padding?: number;
22
+ /** Align the panel to the anchor's width (menus attached to fields). */
23
+ matchWidth?: boolean;
24
+ }
25
+ /** Resolve coordinates for a placement and write them to the panel. */
26
+ export declare function positionOverlay(options: PositionOptions): void;
27
+ /**
28
+ * Keep an overlay placed while the page changes.
29
+ *
30
+ * Returns a disposer. Callers must invoke it when the overlay closes;
31
+ * otherwise the scroll listener outlives the panel.
32
+ */
33
+ export declare function trackOverlay(options: PositionOptions): () => void;
34
+ export declare function nextZIndex(): number;
35
+ /** All focusable descendants of a container, in DOM order. */
36
+ export declare function focusableWithin(container: HTMLElement): HTMLElement[];
37
+ /**
38
+ * Trap Tab focus inside a container (dialogs).
39
+ * Returns a disposer that also restores the previously focused element.
40
+ */
41
+ export declare function trapFocus(container: HTMLElement): () => void;
@@ -0,0 +1,46 @@
1
+ import { Component } from "../component";
2
+ import { type Child } from "../dom";
3
+ import "./dialog.css";
4
+ export interface DialogOptions {
5
+ title: string;
6
+ /** Body content: a string, or any node for rich content. */
7
+ body?: Child;
8
+ confirmLabel?: string;
9
+ cancelLabel?: string;
10
+ /** Fires when the user confirms. */
11
+ onConfirm?: () => void;
12
+ /** Fires when the user cancels, presses Escape or clicks the scrim. */
13
+ onCancel?: () => void;
14
+ }
15
+ /**
16
+ * A modal macOS alert.
17
+ *
18
+ * Three things a dialog must get right, and this one does because it delegates
19
+ * to `trapFocus` from overlay.ts:
20
+ *
21
+ * - Tab cycles inside the dialog instead of escaping to the page behind it.
22
+ * - Escape closes it, which is what a mac user reaches for.
23
+ * - Focus returns to whatever had it before the dialog opened.
24
+ *
25
+ * The scrim absorbs clicks on the "outside", treating them as a cancel.
26
+ */
27
+ export declare class Dialog extends Component<HTMLDivElement> {
28
+ protected readonly root: HTMLDivElement;
29
+ private readonly scrim;
30
+ private readonly onCancel?;
31
+ private readonly onConfirm?;
32
+ private releaseFocus;
33
+ private opened;
34
+ constructor(options: DialogOptions);
35
+ /** Show the dialog modally. */
36
+ open(): void;
37
+ /** Hide the dialog and restore focus. */
38
+ close(): void;
39
+ /** Confirm and close. */
40
+ confirm(): void;
41
+ /** Cancel and close. */
42
+ cancel(): void;
43
+ /** Whether the dialog is on screen. */
44
+ get isOpen(): boolean;
45
+ destroy(): void;
46
+ }
@@ -0,0 +1,58 @@
1
+ import { Component } from "../component";
2
+ import { type Placement } from "../overlay";
3
+ import "./menu.css";
4
+ export interface MenuItem {
5
+ label: string;
6
+ value: string;
7
+ /** Leading SVG path data. */
8
+ icon?: string;
9
+ disabled?: boolean;
10
+ /** Show a checkmark (set `role=menuitemcheckbox` semantics). */
11
+ checked?: boolean;
12
+ /** Draw a separator instead of an item. */
13
+ separator?: boolean;
14
+ /** Grey trailing text, e.g. "⌘S". */
15
+ shortcut?: string;
16
+ }
17
+ export interface MenuOptions {
18
+ items: MenuItem[];
19
+ placement?: Placement;
20
+ /** Fires with the chosen value. */
21
+ onSelect?: (value: string) => void;
22
+ }
23
+ /**
24
+ * A macOS pop-up menu.
25
+ *
26
+ * Appended to `document.body` rather than the trigger's parent: a menu inside a
27
+ * scrolling pane or an `overflow: hidden` card would otherwise be clipped.
28
+ *
29
+ * Keyboard support follows the native menu: arrows move the highlight, Home/End
30
+ * jump, Enter selects, Escape closes and returns focus to the trigger.
31
+ */
32
+ export declare class Menu extends Component<HTMLDivElement> {
33
+ protected readonly root: HTMLDivElement;
34
+ private readonly items;
35
+ private readonly onSelect?;
36
+ private readonly menuItems;
37
+ private opened;
38
+ private disposeTracking;
39
+ private restoreFocusTo;
40
+ private highlighted;
41
+ constructor(options: MenuOptions);
42
+ /** Open the menu anchored to an element. */
43
+ open(anchor: HTMLElement): void;
44
+ /** Close the menu and restore focus to whatever opened it. */
45
+ close(): void;
46
+ /** Whether the menu is currently open. */
47
+ get isOpen(): boolean;
48
+ private choose;
49
+ /** Move the keyboard highlight to an index, skipping disabled and separator
50
+ rows. */
51
+ private highlight;
52
+ /** Step the highlight by ±1, wrapping and skipping disabled entries. */
53
+ private step;
54
+ private onKeyDown;
55
+ destroy(): void;
56
+ }
57
+ /** Convenience: build a menu, open it at an anchor, and release it on close. */
58
+ export declare function showMenu(anchor: HTMLElement, options: MenuOptions): Menu;
@@ -0,0 +1,90 @@
1
+ import { Component } from "../component";
2
+ import { type Placement } from "../overlay";
3
+ import "./popover.css";
4
+ export interface PopoverOptions {
5
+ /** Arbitrary content: a string becomes text, a node is adopted as-is. */
6
+ content: Node | string;
7
+ /** Preferred side; flips automatically when there is no room. */
8
+ placement?: Placement;
9
+ /** Gap between the anchor and the panel, in px. */
10
+ offset?: number;
11
+ /** Draw a pointer aimed at the anchor. */
12
+ arrow?: boolean;
13
+ /** Accessible name for the dialog; a popover has no title of its own. */
14
+ ariaLabel?: string;
15
+ /** Fires whenever the popover closes, whatever closed it. */
16
+ onClose?: () => void;
17
+ }
18
+ /**
19
+ * A macOS popover: arbitrary content attached to an anchor element.
20
+ *
21
+ * This is the generalisation of `Menu` — the same anchoring plumbing, but the
22
+ * content is the caller's and the panel is non-modal, so the page behind stays
23
+ * interactive. Three consequences shape the implementation:
24
+ *
25
+ * - The panel is appended to `document.body`, never inside the trigger: a
26
+ * popover opened from a scrolling pane or an `overflow: hidden` card would
27
+ * otherwise be clipped.
28
+ * - It carries `role="dialog"` with `aria-modal="false"`, so assistive tech
29
+ * announces a dialog without promising that the rest of the page is inert.
30
+ * - Focus returns to the anchor on close, but only while the popover still owns
31
+ * it — clicking another field must not yank focus back.
32
+ *
33
+ * The markup is root → surface → body. The split exists because
34
+ * `positionOverlay` measures with `getBoundingClientRect`, which *includes*
35
+ * transforms: any entrance transform on the element being positioned would make
36
+ * the overlay place itself against a shrunken box. The root therefore stays
37
+ * untransformed and is what overlay.ts positions; only `__surface` animates.
38
+ */
39
+ export declare class Popover extends Component<HTMLDivElement> {
40
+ protected readonly root: HTMLDivElement;
41
+ private readonly placement;
42
+ private readonly offset;
43
+ private readonly onClose?;
44
+ private readonly surface;
45
+ private readonly arrowNode;
46
+ private anchor;
47
+ private opened;
48
+ private disposeTracking;
49
+ private closeTimer;
50
+ private arrowFrame;
51
+ constructor(options: PopoverOptions);
52
+ /** Show the popover next to an anchor. Re-opening with a new anchor moves it. */
53
+ open(anchor: HTMLElement): void;
54
+ /** Hide the popover and hand focus back to the anchor. */
55
+ close(): void;
56
+ /** Whether the popover is currently on screen. */
57
+ get isOpen(): boolean;
58
+ /** Position options shared by the one-shot placement and the tracker. */
59
+ private positionOptions;
60
+ /** Resolve the panel against the current anchor. */
61
+ private place;
62
+ /**
63
+ * Re-aim the pointer one frame after a scroll or resize.
64
+ *
65
+ * Placement is itself rAF-throttled inside `trackOverlay`, and this callback
66
+ * may run before that pass, so it re-places first: measuring the panel where
67
+ * it is about to be drawn is the only reading that matters.
68
+ */
69
+ private scheduleArrowUpdate;
70
+ private cancelArrowUpdate;
71
+ /**
72
+ * Aim the pointer at the anchor's centre.
73
+ *
74
+ * Only the position *along* the panel's edge is measured here; CSS owns the
75
+ * side, taken from `data-side`, which positionOverlay sets. Both ends are
76
+ * clamped by half the arrow's own width, so a rotated square never pokes
77
+ * past a rounded corner — and no pixel constant has to be invented for it.
78
+ *
79
+ * `offsetLeft`/`offsetTop` are used instead of a rect because they report
80
+ * layout position and ignore transforms: the surface is mid-transition when
81
+ * this first runs, and a rect would describe the sliding box rather than the
82
+ * box the pointer is attached to. `clientLeft`/`clientWidth` then move the
83
+ * origin to the padding edge, which is what an absolutely positioned child
84
+ * is offset from — so the surface's own hairline border cannot skew it.
85
+ */
86
+ private positionArrow;
87
+ /** Put focus on the first control inside, or on the panel itself. */
88
+ private focusInto;
89
+ destroy(): void;
90
+ }
@@ -0,0 +1,56 @@
1
+ import { Component } from "../component";
2
+ import { type Child } from "../dom";
3
+ import "./sheet.css";
4
+ export interface SheetOptions {
5
+ /** Sheet heading, also the accessible name of the dialog. */
6
+ title: string;
7
+ /** Body content: a string, or any node for rich content. */
8
+ body?: Child;
9
+ confirmLabel?: string;
10
+ cancelLabel?: string;
11
+ /** Fires when the user confirms. */
12
+ onConfirm?: () => void;
13
+ /** Fires when the user cancels, presses Escape or clicks the scrim. */
14
+ onCancel?: () => void;
15
+ }
16
+ /**
17
+ * A macOS sheet: a modal attached to the window's top edge that slides down.
18
+ *
19
+ * Sheets differ from `Dialog` in two ways that shape the implementation:
20
+ *
21
+ * - **Geometry.** A dialog is centred in the viewport; a sheet is pinned to the
22
+ * top and grows downward, so the window's title bar stays visible behind it.
23
+ * That is the point of the pattern: it reads as belonging to *this* window
24
+ * rather than to the application.
25
+ * - **Motion.** The entrance is `transform: translateY()` on the panel, never
26
+ * `top` or `height`. A compositor-animated transform leaves the sheet's own
27
+ * layout untouched, so a form inside it does not reflow while it slides in.
28
+ *
29
+ * Modal behaviour is delegated to `trapFocus` from overlay.ts: Tab cycles inside
30
+ * the sheet, and the disposer restores focus to whatever had it before.
31
+ */
32
+ export declare class Sheet extends Component<HTMLDivElement> {
33
+ protected readonly root: HTMLDivElement;
34
+ private readonly scrim;
35
+ private readonly panel;
36
+ private readonly bodyNode;
37
+ private readonly onConfirm?;
38
+ private readonly onCancel?;
39
+ private releaseFocus;
40
+ private opened;
41
+ private closeTimer;
42
+ constructor(options: SheetOptions);
43
+ /** Slide the sheet in. Idempotent. */
44
+ open(): void;
45
+ /** Slide the sheet out and restore focus. Idempotent. */
46
+ close(): void;
47
+ /** Confirm and close. */
48
+ confirm(): void;
49
+ /** Cancel and close. */
50
+ cancel(): void;
51
+ /** Whether the sheet is on screen. */
52
+ get isOpen(): boolean;
53
+ /** The body element, for callers that fill it after construction. */
54
+ get content(): HTMLElement;
55
+ destroy(): void;
56
+ }
@@ -0,0 +1,37 @@
1
+ import { Component } from "../component";
2
+ import "./toast.css";
3
+ export type ToastVariant = "default" | "success" | "error";
4
+ export interface ToastOptions {
5
+ message?: string;
6
+ /** Secondary line under the message. */
7
+ detail?: string;
8
+ /** Auto-dismiss delay in ms; 0 keeps it until dismissed. */
9
+ duration?: number;
10
+ variant?: ToastVariant;
11
+ }
12
+ /**
13
+ * A macOS-style toast.
14
+ *
15
+ * Announced through `role="status"` with `aria-live="polite"`, so screen
16
+ * readers read it without stealing focus from whatever the user is doing.
17
+ */
18
+ export declare class Toast extends Component<HTMLDivElement> {
19
+ protected readonly root: HTMLDivElement;
20
+ private readonly messageNode;
21
+ private readonly detailNode;
22
+ private duration;
23
+ private timer;
24
+ private visible;
25
+ constructor(options?: ToastOptions);
26
+ /** Show the toast, replacing its text. Mounts itself into the shared stack. */
27
+ show(message?: string, detail?: string): void;
28
+ /** Update the auto-dismiss delay for subsequent shows. */
29
+ setDuration(duration: number): void;
30
+ /** Dismiss immediately. */
31
+ hide(): void;
32
+ private scheduleDismiss;
33
+ private clearTimer;
34
+ destroy(): void;
35
+ }
36
+ /** Fire-and-forget helper for the common case. */
37
+ export declare function showToast(message: string, options?: Omit<ToastOptions, "message">): Toast;
@@ -0,0 +1,48 @@
1
+ import { Component } from "../component";
2
+ import { type Placement } from "../overlay";
3
+ import "./tooltip.css";
4
+ export interface TooltipOptions {
5
+ content: string;
6
+ placement?: Placement;
7
+ /** Delay before showing, in ms. macOS uses a noticeable pause. */
8
+ delay?: number;
9
+ }
10
+ /**
11
+ * A macOS tooltip.
12
+ *
13
+ * Two things make tooltips usable that hand-written ones usually miss:
14
+ *
15
+ * 1. **Focus triggers it too.** Keyboard and screen-reader users never hover,
16
+ * so the tooltip opens on focus as well and the trigger is linked with
17
+ * `aria-describedby`.
18
+ * 2. **The delay cancels.** A quick pointer sweep across a toolbar must not
19
+ * flash a tooltip on every button, so leaving clears the pending timer.
20
+ */
21
+ export declare class Tooltip extends Component<HTMLDivElement> {
22
+ protected readonly root: HTMLDivElement;
23
+ private readonly content;
24
+ private readonly placement;
25
+ private readonly delay;
26
+ private trigger;
27
+ private disposeTracking;
28
+ private showTimer;
29
+ private visible;
30
+ /**
31
+ * Listeners bound to the current trigger. Tracked separately from the
32
+ * component's own list because `attachTo` can rebind many times over one
33
+ * component's life; `listen()` alone would accumulate every binding.
34
+ */
35
+ private triggerListeners;
36
+ constructor(options: TooltipOptions);
37
+ /** Bind the tooltip to an element. A tooltip serves one trigger at a time. */
38
+ attachTo(trigger: HTMLElement): void;
39
+ /** Unbind from the current trigger and remove the tooltip. */
40
+ detach(): void;
41
+ private schedule;
42
+ private cancelTimer;
43
+ /** Show the bubble next to the trigger. */
44
+ open(): void;
45
+ /** Hide the bubble. */
46
+ close(): void;
47
+ destroy(): void;
48
+ }
@@ -0,0 +1,2 @@
1
+ /** Side-effect only stylesheet. Import it once, for its rules. */
2
+ export {};
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Runtime theme system.
3
+ *
4
+ * Two-layer tokens (see tokens.css) split the problem in half:
5
+ *
6
+ * - The built-in `macos-dark` / `macos-light` appearances are **pure CSS**. They
7
+ * apply through `[data-appearance]` with no JavaScript at all, so the first
8
+ * paint is already correct — a theme applied by script would flash the wrong
9
+ * colours for one frame.
10
+ *
11
+ * - Everything else is a *custom* theme: an appearance plus a handful of
12
+ * semantic-token overrides. Overrides land as inline custom properties on
13
+ * <html>, which beat the stylesheet, so a brand theme ships 3 values instead
14
+ * of 60.
15
+ *
16
+ * The rule that keeps this honest: custom themes may only override **semantic**
17
+ * tokens (`accent`, `bg-window`, …), never base ones (`base-gray-800`). Base
18
+ * tokens are shared by both appearances; overriding one would silently damage
19
+ * the other.
20
+ */
21
+ export type Appearance = "light" | "dark";
22
+ export interface ThemeDefinition {
23
+ /** Unique name, written to `data-theme`. */
24
+ name: string;
25
+ /** Which built-in appearance the theme starts from. */
26
+ appearance: Appearance;
27
+ /**
28
+ * Semantic-token overrides. Keys are written **without** the `--mac-`
29
+ * prefix for brevity, e.g. `{accent: "#ff6b35"}` not
30
+ * `{"--mac-accent": "#ff6b35"}`.
31
+ */
32
+ tokens?: Record<string, string>;
33
+ }
34
+ /** The built-in appearances. No tokens: their values live in tokens.css. */
35
+ export declare const BUILTIN_THEMES: ThemeDefinition[];
36
+ /**
37
+ * Owns the appearance on <html> and any custom-token overrides.
38
+ *
39
+ * One instance per document; `themeManager` below is the shared one.
40
+ */
41
+ export declare class ThemeManager {
42
+ private readonly root;
43
+ private readonly themes;
44
+ /** Custom properties this manager wrote, so they can be removed cleanly. */
45
+ private applied;
46
+ private current;
47
+ private mediaQuery;
48
+ private systemListener;
49
+ private listeners;
50
+ constructor(root?: HTMLElement);
51
+ /** Register a custom theme. Throws on an empty or duplicate name. */
52
+ register(theme: ThemeDefinition): void;
53
+ /** Remove a custom theme. Built-in appearances cannot be removed. */
54
+ unregister(name: string): void;
55
+ /** Names of every registered theme. */
56
+ list(): string[];
57
+ /** The theme currently applied. */
58
+ get active(): ThemeDefinition;
59
+ /**
60
+ * Apply a theme by name.
61
+ *
62
+ * Applies the appearance first (pure CSS takes over), then the token
63
+ * overrides. Re-applying the same theme is a no-op when nothing changed.
64
+ */
65
+ apply(name: string): void;
66
+ /**
67
+ * Follow the OS appearance: dark when the system is dark, light otherwise.
68
+ * Re-applies automatically when the user flips the system setting.
69
+ */
70
+ followSystem(): void;
71
+ /** True while the OS appearance drives the theme. */
72
+ get isFollowingSystem(): boolean;
73
+ /** Subscribe to theme changes. Returns an unsubscribe function. */
74
+ onChange(listener: (theme: ThemeDefinition) => void): () => void;
75
+ /**
76
+ * Export the active theme as a definition.
77
+ *
78
+ * Reads the computed custom properties, so it captures values that came from
79
+ * the stylesheet as well as inline overrides — useful for "save this look".
80
+ */
81
+ exportActive(): ThemeDefinition;
82
+ /** Drop listeners. Call when tearing the app down. */
83
+ destroy(): void;
84
+ private applyDefinition;
85
+ /** Remove every inline custom property this manager wrote. */
86
+ private clearOverrides;
87
+ private stopFollowingSystem;
88
+ }
89
+ /** The application-wide theme manager. */
90
+ export declare const themeManager: ThemeManager;
91
+ /**
92
+ * Set the density preset ("compact" | "comfortable" | "touch").
93
+ *
94
+ * Density is pure CSS: it only moves `--mac-scale`, which every scalable token
95
+ * multiplies. Removing the attribute hands control back to the media query that
96
+ * enlarges controls on touch devices.
97
+ */
98
+ export declare function setDensity(density: "compact" | "comfortable" | "touch" | null): void;
99
+ /** The density currently in effect, including the automatic touch default. */
100
+ export declare function getDensity(): string;
package/package.json CHANGED
@@ -1,6 +1,57 @@
1
1
  {
2
2
  "name": "@leef-l/macos-ui-kit",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.1",
4
+ "description": "macOS-style UI components in dependency-free TypeScript, for Wails v3 desktop apps and plain web pages",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "macos",
9
+ "ui",
10
+ "components",
11
+ "vanilla",
12
+ "typescript",
13
+ "wails"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/leef-l/macos-ui-kit.git",
18
+ "directory": "packages/macos-ui-kit"
19
+ },
20
+ "main": "./dist/index.js",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js"
27
+ },
28
+ "./style.css": {
29
+ "types": "./dist/style.css.d.ts",
30
+ "default": "./dist/macos-ui-kit.css"
31
+ },
32
+ "./global": "./dist/macos-ui-kit.iife.js",
33
+ "./package.json": "./package.json"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md"
38
+ ],
39
+ "sideEffects": [
40
+ "**/*.css",
41
+ "src/index.ts",
42
+ "dist/index.js"
43
+ ],
44
+ "publishConfig": {
45
+ "registry": "https://registry.npmjs.org",
46
+ "access": "public"
47
+ },
48
+ "scripts": {
49
+ "build": "vite build && tsc -p tsconfig.build.json && node scripts/finalize-dist.mjs",
50
+ "typecheck": "tsc -p tsconfig.json --noEmit",
51
+ "prepublishOnly": "npm run build"
52
+ },
53
+ "devDependencies": {
54
+ "typescript": "^7.0.2",
55
+ "vite": "^8.2.4"
56
+ }
57
+ }