@hanzo/ui 8.0.17 → 8.0.19

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 (33) hide show
  1. package/dist/ThemeToggleNext-LI3SBFW7.cjs +20 -0
  2. package/dist/{ThemeToggleNext-ICB5KL5J.cjs.map → ThemeToggleNext-LI3SBFW7.cjs.map} +1 -1
  3. package/dist/ThemeToggleNext-OMBC67MO.js +5 -0
  4. package/dist/{ThemeToggleNext-7727UR5W.js.map → ThemeToggleNext-OMBC67MO.js.map} +1 -1
  5. package/dist/{chunk-23V6BPHG.js → chunk-3YH7AUKQ.js} +83 -13
  6. package/dist/chunk-3YH7AUKQ.js.map +1 -0
  7. package/dist/chunk-57YEM34R.cjs +35 -0
  8. package/dist/chunk-57YEM34R.cjs.map +1 -0
  9. package/dist/{chunk-YBXVUV7P.cjs → chunk-J3P63OX6.cjs} +6 -4
  10. package/dist/chunk-J3P63OX6.cjs.map +1 -0
  11. package/dist/chunk-P6IHE2O3.js +29 -0
  12. package/dist/chunk-P6IHE2O3.js.map +1 -0
  13. package/dist/{chunk-42CU5GSF.js → chunk-T3UAC7ZZ.js} +6 -4
  14. package/dist/chunk-T3UAC7ZZ.js.map +1 -0
  15. package/dist/{chunk-5MBAHVEO.cjs → chunk-XULZJEW2.cjs} +83 -13
  16. package/dist/chunk-XULZJEW2.cjs.map +1 -0
  17. package/dist/product/Field.d.ts +1 -1
  18. package/dist/product/Filters.d.ts +6 -2
  19. package/dist/product/index.cjs +126 -61
  20. package/dist/product/index.cjs.map +1 -1
  21. package/dist/product/index.d.ts +1 -0
  22. package/dist/product/index.js +61 -15
  23. package/dist/product/index.js.map +1 -1
  24. package/dist/product/instrument.d.ts +33 -0
  25. package/dist/product/social/index.cjs +16 -15
  26. package/dist/product/social/index.js +2 -1
  27. package/package.json +1 -1
  28. package/dist/ThemeToggleNext-7727UR5W.js +0 -4
  29. package/dist/ThemeToggleNext-ICB5KL5J.cjs +0 -19
  30. package/dist/chunk-23V6BPHG.js.map +0 -1
  31. package/dist/chunk-42CU5GSF.js.map +0 -1
  32. package/dist/chunk-5MBAHVEO.cjs.map +0 -1
  33. package/dist/chunk-YBXVUV7P.cjs.map +0 -1
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ 'use strict';
3
+
4
+ var react = require('react');
5
+ var telemetry = require('@hanzogui/telemetry');
6
+
7
+ // src/product/instrument.ts
8
+ var SurfaceContext = react.createContext(void 0);
9
+ var InstrumentSurface = SurfaceContext.Provider;
10
+ var useSurface = () => react.useContext(SurfaceContext);
11
+ function emit(e) {
12
+ telemetry.track(telemetry.EVENTS.FEATURE_USED, {
13
+ component: e.component,
14
+ action: e.action,
15
+ ...e.id !== void 0 ? { id: e.id } : null,
16
+ ...e.value !== void 0 ? { value: e.value } : null,
17
+ ...e.surface !== void 0 ? { surface: e.surface } : null
18
+ });
19
+ }
20
+ function useEmit() {
21
+ const surface = useSurface();
22
+ return react.useCallback(
23
+ (e) => emit(surface !== void 0 && e.surface === void 0 ? { ...e, surface } : e),
24
+ [surface]
25
+ );
26
+ }
27
+ var textSize = (s) => s ? s.length : 0;
28
+
29
+ exports.InstrumentSurface = InstrumentSurface;
30
+ exports.emit = emit;
31
+ exports.textSize = textSize;
32
+ exports.useEmit = useEmit;
33
+ exports.useSurface = useSurface;
34
+ //# sourceMappingURL=chunk-57YEM34R.cjs.map
35
+ //# sourceMappingURL=chunk-57YEM34R.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/product/instrument.ts"],"names":["createContext","useContext","track","EVENTS","useCallback"],"mappings":";;;;;;AAkEA,IAAM,cAAA,GAAiBA,oBAAkC,MAAS,CAAA;AAI3D,IAAM,oBAAoB,cAAA,CAAe;AAGzC,IAAM,UAAA,GAAa,MAA0BC,gBAAA,CAAW,cAAc;AAItE,SAAS,KAAK,CAAA,EAAkB;AACrC,EAAAC,eAAA,CAAMC,iBAAO,YAAA,EAAc;AAAA,IACzB,WAAW,CAAA,CAAE,SAAA;AAAA,IACb,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,GAAI,EAAE,EAAA,KAAO,MAAA,GAAY,EAAE,EAAA,EAAI,CAAA,CAAE,IAAG,GAAI,IAAA;AAAA,IACxC,GAAI,EAAE,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,CAAA,CAAE,OAAM,GAAI,IAAA;AAAA,IACjD,GAAI,EAAE,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,EAAS,CAAA,CAAE,SAAQ,GAAI;AAAA,GACxD,CAAA;AACH;AAIO,SAAS,OAAA,GAAgC;AAC9C,EAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,EAAA,OAAOC,iBAAA;AAAA,IACL,CAAC,CAAA,KAAe,IAAA,CAAK,OAAA,KAAY,MAAA,IAAa,CAAA,CAAE,OAAA,KAAY,MAAA,GAAY,EAAE,GAAG,CAAA,EAAG,OAAA,KAAY,CAAC,CAAA;AAAA,IAC7F,CAAC,OAAO;AAAA,GACV;AACF;AAKO,IAAM,QAAA,GAAW,CAAC,CAAA,KAAmC,CAAA,GAAI,EAAE,MAAA,GAAS","file":"chunk-57YEM34R.cjs","sourcesContent":["// The ONE place a shared component says what a user just did.\n//\n// Instrumenting the COMPONENT beats instrumenting 100 apps: a product that\n// renders `DataTable`, `PrimaryButton`, `SlideOver`, `ConfirmDelete`, `Field*`,\n// `ComboBox`, `Segmented`, `SearchInput`, `OrgSwitcher`, `MenuItemView`,\n// `EmptyState` or `ThemeToggle` reports the SAME interaction vocabulary with\n// zero app code. Nothing to wire, nothing to remember, nothing to drift.\n//\n// import { emit, useEmit, InstrumentSurface } from '@hanzo/ui'\n// <InstrumentSurface name=\"billing\">…</InstrumentSurface> // optional context\n//\n// ONE event name, structured properties — the taxonomy in @hanzo/event is a\n// CLOSED set, and its own rule for a product-specific moment is \"the `action`\n// property, never a new event name\". So every UI interaction is\n// EVENTS.FEATURE_USED carrying { component, action, surface, id, value }.\n// Funnel moments (signup, checkout, deploy) keep their own dedicated names and\n// are emitted by the surface that owns them, not by a button.\n//\n// One stream: `track` from @hanzogui/telemetry — already a dependency of this\n// package and already THE front door (POST api.hanzo.ai/v1/event). No second\n// client, no CDN script, no `window` poking. It is SSR-safe, consent-aware,\n// honors DNT/GPC, and is fail-soft by construction, so this module inherits all\n// of that and adds no error policy of its own.\n\nimport { createContext, useCallback, useContext } from 'react'\n// ONE dependency edge: @hanzogui/telemetry owns the transport AND re-exports the\n// @hanzo/event vocabulary, so this package never names a second source of truth.\nimport { EVENTS, track } from '@hanzogui/telemetry'\n\n/** What a user did to a component. A closed set — it is the verb half of the\n * vocabulary, and a closed verb set is what makes cross-product funnels join. */\nexport type UiAction =\n | 'click'\n | 'open'\n | 'close'\n | 'select'\n | 'change'\n | 'sort'\n | 'filter'\n | 'search'\n | 'confirm'\n | 'cancel'\n | 'dismiss'\n | 'expand'\n | 'view'\n | 'error'\n\n/** One interaction. `component` + `action` are required; everything else is the\n * detail a lens groups by. Values are scrubbed downstream by @hanzo/event. */\nexport interface UiEvent {\n /** The shared component that observed it — `DataTable`, `PrimaryButton`, … */\n component: string\n action: UiAction\n /** The thing acted on: a column key, an option value, a menu item id. */\n id?: string\n /** A small scalar detail (sort direction, boolean state, count). Never PII —\n * free-text values (search terms, field contents) are reported as a LENGTH,\n * by the caller, not as the text. */\n value?: string | number | boolean\n /** The product area, when the app declared one via `<InstrumentSurface/>`. */\n surface?: string\n}\n\n/** The enclosing product area (route/module). Optional: with no provider the\n * events still carry component+action, and the product+path already come from\n * the telemetry client. */\nconst SurfaceContext = createContext<string | undefined>(undefined)\n\n/** Names the product area every shared component inside it reports under.\n * Plain provider, no element — it must not perturb any layout. */\nexport const InstrumentSurface = SurfaceContext.Provider\n\n/** The current product area, or undefined outside a provider. */\nexport const useSurface = (): string | undefined => useContext(SurfaceContext)\n\n/** Report an interaction. Callable from anywhere (module scope, event handler,\n * a non-React host) — the ambient telemetry client is built on first use. */\nexport function emit(e: UiEvent): void {\n track(EVENTS.FEATURE_USED, {\n component: e.component,\n action: e.action,\n ...(e.id !== undefined ? { id: e.id } : null),\n ...(e.value !== undefined ? { value: e.value } : null),\n ...(e.surface !== undefined ? { surface: e.surface } : null),\n })\n}\n\n/** `emit` with the surrounding `<InstrumentSurface/>` already bound — what the\n * shared components themselves use, so an app never labels a surface twice. */\nexport function useEmit(): (e: UiEvent) => void {\n const surface = useSurface()\n return useCallback(\n (e: UiEvent) => emit(surface !== undefined && e.surface === undefined ? { ...e, surface } : e),\n [surface],\n )\n}\n\n/** Free text (a search box, a text field) is reported as its LENGTH — the shape\n * of the behaviour without the content. One helper so no component invents its\n * own idea of \"safe to send\". */\nexport const textSize = (s: string | undefined): number => (s ? s.length : 0)\n"]}
@@ -1,13 +1,13 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
+ var chunk57YEM34R_cjs = require('./chunk-57YEM34R.cjs');
4
5
  var nextTheme = require('@hanzogui/next-theme');
5
6
  var react = require('react');
6
7
  var gui = require('@hanzo/gui');
7
8
  var lucideIcons2 = require('@hanzogui/lucide-icons-2');
8
9
  var jsxRuntime = require('react/jsx-runtime');
9
10
 
10
- // src/product/ThemeToggleNext.tsx
11
11
  var STORAGE_KEY = "theme";
12
12
  function readDomTheme() {
13
13
  if (typeof document === "undefined") return void 0;
@@ -44,8 +44,10 @@ function AgnosticThemeToggle({
44
44
  const [internal, setInternal] = react.useState(() => theme ?? defaultTheme ?? readDomTheme() ?? "dark");
45
45
  const current = controlled ? theme : internal;
46
46
  const isDark = current === "dark";
47
+ const track = chunk57YEM34R_cjs.useEmit();
47
48
  const toggle = () => {
48
49
  const next = isDark ? "light" : "dark";
50
+ track({ component: "ThemeToggle", action: "change", value: next });
49
51
  if (!controlled) {
50
52
  setInternal(next);
51
53
  applyDomTheme(next);
@@ -64,7 +66,7 @@ function AgnosticThemeToggle({
64
66
  }
65
67
  );
66
68
  }
67
- var NextThemeToggle = react.lazy(() => import('./ThemeToggleNext-ICB5KL5J.cjs'));
69
+ var NextThemeToggle = react.lazy(() => import('./ThemeToggleNext-LI3SBFW7.cjs'));
68
70
  var NextThemeBoundary = class extends react.Component {
69
71
  state = { failed: false };
70
72
  static getDerivedStateFromError() {
@@ -99,5 +101,5 @@ exports.AgnosticThemeToggle = AgnosticThemeToggle;
99
101
  exports.ThemeToggle = ThemeToggle;
100
102
  exports.ThemeToggleNext = ThemeToggleNext;
101
103
  exports.ThemeToggleNext_default = ThemeToggleNext_default;
102
- //# sourceMappingURL=chunk-YBXVUV7P.cjs.map
103
- //# sourceMappingURL=chunk-YBXVUV7P.cjs.map
104
+ //# sourceMappingURL=chunk-J3P63OX6.cjs.map
105
+ //# sourceMappingURL=chunk-J3P63OX6.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/product/ThemeToggle.tsx","../src/product/ThemeToggleNext.tsx"],"names":["useState","useEmit","jsx","Button","Sun","Moon","lazy","Component","Suspense","useThemeSetting"],"mappings":"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","file":"chunk-J3P63OX6.cjs","sourcesContent":["'use client'\n\n/**\n * ThemeToggle — flips between dark and light. Framework-agnostic by default so\n * Vite / Tauri / Express hosts can use it with NO Next dependency:\n *\n * • Controlled: <ThemeToggle theme={theme} onToggle={setTheme} /> (host owns theme)\n * • Uncontrolled (agnostic): <ThemeToggle onThemeChange={fn} /> (toggles the DOM\n * `.dark` class + persists to localStorage)\n * • Uncontrolled (no props): <ThemeToggle /> (console/Next —\n * falls back to the OPTIONAL @hanzogui/next-theme path;\n * if it cannot load, degrades to the agnostic DOM toggle)\n *\n * Shows the action's target icon (a sun in dark mode, a moon in light mode).\n */\nimport type { ReactNode } from 'react'\nimport { Component, Suspense, lazy, useState } from 'react'\nimport { Button } from '@hanzo/gui'\nimport { Moon, Sun } from '@hanzogui/lucide-icons-2'\n\nimport { useEmit } from './instrument'\n\nexport type ThemeMode = 'light' | 'dark'\n\nexport type ThemeToggleProps = {\n /** Controlled theme. Pair with `onToggle`/`onThemeChange` to fully control it. */\n theme?: ThemeMode\n /** Seed for uncontrolled mode (default: read from the DOM `.dark` class, else 'dark'). */\n defaultTheme?: ThemeMode\n /** Called with the next theme on toggle (alias of `onThemeChange`). */\n onToggle?: (next: ThemeMode) => void\n /** Called with the next theme on toggle. */\n onThemeChange?: (next: ThemeMode) => void\n /** @hanzo/gui Button size token (default \"$2\"). */\n size?: string\n /** aria-label override. */\n label?: string\n}\n\nconst STORAGE_KEY = 'theme'\n\nfunction readDomTheme(): ThemeMode | undefined {\n if (typeof document === 'undefined') return undefined\n const el = document.documentElement\n if (el.classList.contains('dark')) return 'dark'\n if (el.classList.contains('light')) return 'light'\n try {\n const ls = window.localStorage.getItem(STORAGE_KEY)\n if (ls === 'light' || ls === 'dark') return ls\n } catch {\n /* localStorage may be unavailable */\n }\n return undefined\n}\n\nfunction applyDomTheme(next: ThemeMode): void {\n if (typeof document === 'undefined') return\n const el = document.documentElement\n el.classList.toggle('dark', next === 'dark')\n el.classList.toggle('light', next === 'light')\n el.style.colorScheme = next\n try {\n window.localStorage.setItem(STORAGE_KEY, next)\n } catch {\n /* ignore */\n }\n}\n\n/**\n * The framework-agnostic toggle button. Controlled when `theme` is provided; otherwise\n * self-managed via the DOM `.dark` class. No framework dependency — safe on any host.\n */\nexport function AgnosticThemeToggle({\n theme,\n defaultTheme,\n onToggle,\n onThemeChange,\n size = '$2',\n label,\n}: ThemeToggleProps) {\n const controlled = theme !== undefined\n const [internal, setInternal] = useState<ThemeMode>(() => theme ?? defaultTheme ?? readDomTheme() ?? 'dark')\n const current: ThemeMode = controlled ? (theme as ThemeMode) : internal\n const isDark = current === 'dark'\n const track = useEmit()\n\n const toggle = () => {\n const next: ThemeMode = isDark ? 'light' : 'dark'\n track({ component: 'ThemeToggle', action: 'change', value: next })\n if (!controlled) {\n setInternal(next)\n applyDomTheme(next)\n }\n onToggle?.(next)\n onThemeChange?.(next)\n }\n\n return (\n <Button\n size={size as never}\n chromeless\n icon={isDark ? <Sun size={16} /> : <Moon size={16} />}\n onPress={toggle}\n aria-label={label ?? (isDark ? 'Switch to light theme' : 'Switch to dark theme')}\n />\n )\n}\n\n// @hanzogui/next-theme is an OPTIONAL dependency — loaded only on the uncontrolled,\n// no-props path (console/Next). Lazily code-split so non-Next hosts never need it at\n// runtime; if it fails to load, the boundary below degrades to the agnostic toggle.\nconst NextThemeToggle = lazy(() => import('./ThemeToggleNext'))\n\nclass NextThemeBoundary extends Component<{ fallback: ReactNode; children: ReactNode }, { failed: boolean }> {\n state = { failed: false }\n static getDerivedStateFromError() {\n return { failed: true }\n }\n render() {\n return this.state.failed ? this.props.fallback : this.props.children\n }\n}\n\nexport function ThemeToggle(props: ThemeToggleProps) {\n const injected =\n props.theme !== undefined || props.onToggle !== undefined || props.onThemeChange !== undefined\n if (injected) return <AgnosticThemeToggle {...props} />\n\n // No props → keep the existing console/Next behaviour via the optional next-theme\n // path, degrading to the agnostic DOM toggle if it is not installed.\n const fallback = <AgnosticThemeToggle {...props} />\n return (\n <NextThemeBoundary fallback={fallback}>\n <Suspense fallback={fallback}>\n <NextThemeToggle size={props.size} label={props.label} />\n </Suspense>\n </NextThemeBoundary>\n )\n}\n","'use client'\n\n/**\n * ThemeToggleNext — the @hanzogui/next-theme binding (console / Next). This is the ONLY\n * module that imports @hanzogui/next-theme, so it stays an OPTIONAL dependency: the base\n * ThemeToggle lazy-loads it (default export) and non-Next hosts never pull it in unless\n * they import this component directly. UI is delegated to the framework-agnostic button\n * in controlled mode, so every toggle looks identical.\n */\nimport { useThemeSetting } from '@hanzogui/next-theme'\nimport { AgnosticThemeToggle, type ThemeToggleProps } from './ThemeToggle'\n\nfunction ThemeToggleNext({ size, label }: Pick<ThemeToggleProps, 'size' | 'label'>) {\n const { current, resolvedTheme, set } = useThemeSetting()\n const isDark = (resolvedTheme ?? current ?? 'dark') !== 'light'\n return (\n <AgnosticThemeToggle\n theme={isDark ? 'dark' : 'light'}\n onToggle={(next) => set(next)}\n size={size}\n label={label}\n />\n )\n}\n\nexport default ThemeToggleNext\nexport { ThemeToggleNext }\n"]}
@@ -0,0 +1,29 @@
1
+ 'use client';
2
+ import { createContext, useContext, useCallback } from 'react';
3
+ import { track, EVENTS } from '@hanzogui/telemetry';
4
+
5
+ // src/product/instrument.ts
6
+ var SurfaceContext = createContext(void 0);
7
+ var InstrumentSurface = SurfaceContext.Provider;
8
+ var useSurface = () => useContext(SurfaceContext);
9
+ function emit(e) {
10
+ track(EVENTS.FEATURE_USED, {
11
+ component: e.component,
12
+ action: e.action,
13
+ ...e.id !== void 0 ? { id: e.id } : null,
14
+ ...e.value !== void 0 ? { value: e.value } : null,
15
+ ...e.surface !== void 0 ? { surface: e.surface } : null
16
+ });
17
+ }
18
+ function useEmit() {
19
+ const surface = useSurface();
20
+ return useCallback(
21
+ (e) => emit(surface !== void 0 && e.surface === void 0 ? { ...e, surface } : e),
22
+ [surface]
23
+ );
24
+ }
25
+ var textSize = (s) => s ? s.length : 0;
26
+
27
+ export { InstrumentSurface, emit, textSize, useEmit, useSurface };
28
+ //# sourceMappingURL=chunk-P6IHE2O3.js.map
29
+ //# sourceMappingURL=chunk-P6IHE2O3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/product/instrument.ts"],"names":[],"mappings":";;;;AAkEA,IAAM,cAAA,GAAiB,cAAkC,MAAS,CAAA;AAI3D,IAAM,oBAAoB,cAAA,CAAe;AAGzC,IAAM,UAAA,GAAa,MAA0B,UAAA,CAAW,cAAc;AAItE,SAAS,KAAK,CAAA,EAAkB;AACrC,EAAA,KAAA,CAAM,OAAO,YAAA,EAAc;AAAA,IACzB,WAAW,CAAA,CAAE,SAAA;AAAA,IACb,QAAQ,CAAA,CAAE,MAAA;AAAA,IACV,GAAI,EAAE,EAAA,KAAO,MAAA,GAAY,EAAE,EAAA,EAAI,CAAA,CAAE,IAAG,GAAI,IAAA;AAAA,IACxC,GAAI,EAAE,KAAA,KAAU,MAAA,GAAY,EAAE,KAAA,EAAO,CAAA,CAAE,OAAM,GAAI,IAAA;AAAA,IACjD,GAAI,EAAE,OAAA,KAAY,MAAA,GAAY,EAAE,OAAA,EAAS,CAAA,CAAE,SAAQ,GAAI;AAAA,GACxD,CAAA;AACH;AAIO,SAAS,OAAA,GAAgC;AAC9C,EAAA,MAAM,UAAU,UAAA,EAAW;AAC3B,EAAA,OAAO,WAAA;AAAA,IACL,CAAC,CAAA,KAAe,IAAA,CAAK,OAAA,KAAY,MAAA,IAAa,CAAA,CAAE,OAAA,KAAY,MAAA,GAAY,EAAE,GAAG,CAAA,EAAG,OAAA,KAAY,CAAC,CAAA;AAAA,IAC7F,CAAC,OAAO;AAAA,GACV;AACF;AAKO,IAAM,QAAA,GAAW,CAAC,CAAA,KAAmC,CAAA,GAAI,EAAE,MAAA,GAAS","file":"chunk-P6IHE2O3.js","sourcesContent":["// The ONE place a shared component says what a user just did.\n//\n// Instrumenting the COMPONENT beats instrumenting 100 apps: a product that\n// renders `DataTable`, `PrimaryButton`, `SlideOver`, `ConfirmDelete`, `Field*`,\n// `ComboBox`, `Segmented`, `SearchInput`, `OrgSwitcher`, `MenuItemView`,\n// `EmptyState` or `ThemeToggle` reports the SAME interaction vocabulary with\n// zero app code. Nothing to wire, nothing to remember, nothing to drift.\n//\n// import { emit, useEmit, InstrumentSurface } from '@hanzo/ui'\n// <InstrumentSurface name=\"billing\">…</InstrumentSurface> // optional context\n//\n// ONE event name, structured properties — the taxonomy in @hanzo/event is a\n// CLOSED set, and its own rule for a product-specific moment is \"the `action`\n// property, never a new event name\". So every UI interaction is\n// EVENTS.FEATURE_USED carrying { component, action, surface, id, value }.\n// Funnel moments (signup, checkout, deploy) keep their own dedicated names and\n// are emitted by the surface that owns them, not by a button.\n//\n// One stream: `track` from @hanzogui/telemetry — already a dependency of this\n// package and already THE front door (POST api.hanzo.ai/v1/event). No second\n// client, no CDN script, no `window` poking. It is SSR-safe, consent-aware,\n// honors DNT/GPC, and is fail-soft by construction, so this module inherits all\n// of that and adds no error policy of its own.\n\nimport { createContext, useCallback, useContext } from 'react'\n// ONE dependency edge: @hanzogui/telemetry owns the transport AND re-exports the\n// @hanzo/event vocabulary, so this package never names a second source of truth.\nimport { EVENTS, track } from '@hanzogui/telemetry'\n\n/** What a user did to a component. A closed set — it is the verb half of the\n * vocabulary, and a closed verb set is what makes cross-product funnels join. */\nexport type UiAction =\n | 'click'\n | 'open'\n | 'close'\n | 'select'\n | 'change'\n | 'sort'\n | 'filter'\n | 'search'\n | 'confirm'\n | 'cancel'\n | 'dismiss'\n | 'expand'\n | 'view'\n | 'error'\n\n/** One interaction. `component` + `action` are required; everything else is the\n * detail a lens groups by. Values are scrubbed downstream by @hanzo/event. */\nexport interface UiEvent {\n /** The shared component that observed it — `DataTable`, `PrimaryButton`, … */\n component: string\n action: UiAction\n /** The thing acted on: a column key, an option value, a menu item id. */\n id?: string\n /** A small scalar detail (sort direction, boolean state, count). Never PII —\n * free-text values (search terms, field contents) are reported as a LENGTH,\n * by the caller, not as the text. */\n value?: string | number | boolean\n /** The product area, when the app declared one via `<InstrumentSurface/>`. */\n surface?: string\n}\n\n/** The enclosing product area (route/module). Optional: with no provider the\n * events still carry component+action, and the product+path already come from\n * the telemetry client. */\nconst SurfaceContext = createContext<string | undefined>(undefined)\n\n/** Names the product area every shared component inside it reports under.\n * Plain provider, no element — it must not perturb any layout. */\nexport const InstrumentSurface = SurfaceContext.Provider\n\n/** The current product area, or undefined outside a provider. */\nexport const useSurface = (): string | undefined => useContext(SurfaceContext)\n\n/** Report an interaction. Callable from anywhere (module scope, event handler,\n * a non-React host) — the ambient telemetry client is built on first use. */\nexport function emit(e: UiEvent): void {\n track(EVENTS.FEATURE_USED, {\n component: e.component,\n action: e.action,\n ...(e.id !== undefined ? { id: e.id } : null),\n ...(e.value !== undefined ? { value: e.value } : null),\n ...(e.surface !== undefined ? { surface: e.surface } : null),\n })\n}\n\n/** `emit` with the surrounding `<InstrumentSurface/>` already bound — what the\n * shared components themselves use, so an app never labels a surface twice. */\nexport function useEmit(): (e: UiEvent) => void {\n const surface = useSurface()\n return useCallback(\n (e: UiEvent) => emit(surface !== undefined && e.surface === undefined ? { ...e, surface } : e),\n [surface],\n )\n}\n\n/** Free text (a search box, a text field) is reported as its LENGTH — the shape\n * of the behaviour without the content. One helper so no component invents its\n * own idea of \"safe to send\". */\nexport const textSize = (s: string | undefined): number => (s ? s.length : 0)\n"]}
@@ -1,11 +1,11 @@
1
1
  'use client';
2
+ import { useEmit } from './chunk-P6IHE2O3.js';
2
3
  import { useThemeSetting } from '@hanzogui/next-theme';
3
4
  import { lazy, useState, Suspense, Component } from 'react';
4
5
  import { Button } from '@hanzo/gui';
5
6
  import { Sun, Moon } from '@hanzogui/lucide-icons-2';
6
7
  import { jsx } from 'react/jsx-runtime';
7
8
 
8
- // src/product/ThemeToggleNext.tsx
9
9
  var STORAGE_KEY = "theme";
10
10
  function readDomTheme() {
11
11
  if (typeof document === "undefined") return void 0;
@@ -42,8 +42,10 @@ function AgnosticThemeToggle({
42
42
  const [internal, setInternal] = useState(() => theme ?? defaultTheme ?? readDomTheme() ?? "dark");
43
43
  const current = controlled ? theme : internal;
44
44
  const isDark = current === "dark";
45
+ const track = useEmit();
45
46
  const toggle = () => {
46
47
  const next = isDark ? "light" : "dark";
48
+ track({ component: "ThemeToggle", action: "change", value: next });
47
49
  if (!controlled) {
48
50
  setInternal(next);
49
51
  applyDomTheme(next);
@@ -62,7 +64,7 @@ function AgnosticThemeToggle({
62
64
  }
63
65
  );
64
66
  }
65
- var NextThemeToggle = lazy(() => import('./ThemeToggleNext-7727UR5W.js'));
67
+ var NextThemeToggle = lazy(() => import('./ThemeToggleNext-OMBC67MO.js'));
66
68
  var NextThemeBoundary = class extends Component {
67
69
  state = { failed: false };
68
70
  static getDerivedStateFromError() {
@@ -94,5 +96,5 @@ function ThemeToggleNext({ size, label }) {
94
96
  var ThemeToggleNext_default = ThemeToggleNext;
95
97
 
96
98
  export { AgnosticThemeToggle, ThemeToggle, ThemeToggleNext, ThemeToggleNext_default };
97
- //# sourceMappingURL=chunk-42CU5GSF.js.map
98
- //# sourceMappingURL=chunk-42CU5GSF.js.map
99
+ //# sourceMappingURL=chunk-T3UAC7ZZ.js.map
100
+ //# sourceMappingURL=chunk-T3UAC7ZZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/product/ThemeToggle.tsx","../src/product/ThemeToggleNext.tsx"],"names":["jsx"],"mappings":"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","file":"chunk-T3UAC7ZZ.js","sourcesContent":["'use client'\n\n/**\n * ThemeToggle — flips between dark and light. Framework-agnostic by default so\n * Vite / Tauri / Express hosts can use it with NO Next dependency:\n *\n * • Controlled: <ThemeToggle theme={theme} onToggle={setTheme} /> (host owns theme)\n * • Uncontrolled (agnostic): <ThemeToggle onThemeChange={fn} /> (toggles the DOM\n * `.dark` class + persists to localStorage)\n * • Uncontrolled (no props): <ThemeToggle /> (console/Next —\n * falls back to the OPTIONAL @hanzogui/next-theme path;\n * if it cannot load, degrades to the agnostic DOM toggle)\n *\n * Shows the action's target icon (a sun in dark mode, a moon in light mode).\n */\nimport type { ReactNode } from 'react'\nimport { Component, Suspense, lazy, useState } from 'react'\nimport { Button } from '@hanzo/gui'\nimport { Moon, Sun } from '@hanzogui/lucide-icons-2'\n\nimport { useEmit } from './instrument'\n\nexport type ThemeMode = 'light' | 'dark'\n\nexport type ThemeToggleProps = {\n /** Controlled theme. Pair with `onToggle`/`onThemeChange` to fully control it. */\n theme?: ThemeMode\n /** Seed for uncontrolled mode (default: read from the DOM `.dark` class, else 'dark'). */\n defaultTheme?: ThemeMode\n /** Called with the next theme on toggle (alias of `onThemeChange`). */\n onToggle?: (next: ThemeMode) => void\n /** Called with the next theme on toggle. */\n onThemeChange?: (next: ThemeMode) => void\n /** @hanzo/gui Button size token (default \"$2\"). */\n size?: string\n /** aria-label override. */\n label?: string\n}\n\nconst STORAGE_KEY = 'theme'\n\nfunction readDomTheme(): ThemeMode | undefined {\n if (typeof document === 'undefined') return undefined\n const el = document.documentElement\n if (el.classList.contains('dark')) return 'dark'\n if (el.classList.contains('light')) return 'light'\n try {\n const ls = window.localStorage.getItem(STORAGE_KEY)\n if (ls === 'light' || ls === 'dark') return ls\n } catch {\n /* localStorage may be unavailable */\n }\n return undefined\n}\n\nfunction applyDomTheme(next: ThemeMode): void {\n if (typeof document === 'undefined') return\n const el = document.documentElement\n el.classList.toggle('dark', next === 'dark')\n el.classList.toggle('light', next === 'light')\n el.style.colorScheme = next\n try {\n window.localStorage.setItem(STORAGE_KEY, next)\n } catch {\n /* ignore */\n }\n}\n\n/**\n * The framework-agnostic toggle button. Controlled when `theme` is provided; otherwise\n * self-managed via the DOM `.dark` class. No framework dependency — safe on any host.\n */\nexport function AgnosticThemeToggle({\n theme,\n defaultTheme,\n onToggle,\n onThemeChange,\n size = '$2',\n label,\n}: ThemeToggleProps) {\n const controlled = theme !== undefined\n const [internal, setInternal] = useState<ThemeMode>(() => theme ?? defaultTheme ?? readDomTheme() ?? 'dark')\n const current: ThemeMode = controlled ? (theme as ThemeMode) : internal\n const isDark = current === 'dark'\n const track = useEmit()\n\n const toggle = () => {\n const next: ThemeMode = isDark ? 'light' : 'dark'\n track({ component: 'ThemeToggle', action: 'change', value: next })\n if (!controlled) {\n setInternal(next)\n applyDomTheme(next)\n }\n onToggle?.(next)\n onThemeChange?.(next)\n }\n\n return (\n <Button\n size={size as never}\n chromeless\n icon={isDark ? <Sun size={16} /> : <Moon size={16} />}\n onPress={toggle}\n aria-label={label ?? (isDark ? 'Switch to light theme' : 'Switch to dark theme')}\n />\n )\n}\n\n// @hanzogui/next-theme is an OPTIONAL dependency — loaded only on the uncontrolled,\n// no-props path (console/Next). Lazily code-split so non-Next hosts never need it at\n// runtime; if it fails to load, the boundary below degrades to the agnostic toggle.\nconst NextThemeToggle = lazy(() => import('./ThemeToggleNext'))\n\nclass NextThemeBoundary extends Component<{ fallback: ReactNode; children: ReactNode }, { failed: boolean }> {\n state = { failed: false }\n static getDerivedStateFromError() {\n return { failed: true }\n }\n render() {\n return this.state.failed ? this.props.fallback : this.props.children\n }\n}\n\nexport function ThemeToggle(props: ThemeToggleProps) {\n const injected =\n props.theme !== undefined || props.onToggle !== undefined || props.onThemeChange !== undefined\n if (injected) return <AgnosticThemeToggle {...props} />\n\n // No props → keep the existing console/Next behaviour via the optional next-theme\n // path, degrading to the agnostic DOM toggle if it is not installed.\n const fallback = <AgnosticThemeToggle {...props} />\n return (\n <NextThemeBoundary fallback={fallback}>\n <Suspense fallback={fallback}>\n <NextThemeToggle size={props.size} label={props.label} />\n </Suspense>\n </NextThemeBoundary>\n )\n}\n","'use client'\n\n/**\n * ThemeToggleNext — the @hanzogui/next-theme binding (console / Next). This is the ONLY\n * module that imports @hanzogui/next-theme, so it stays an OPTIONAL dependency: the base\n * ThemeToggle lazy-loads it (default export) and non-Next hosts never pull it in unless\n * they import this component directly. UI is delegated to the framework-agnostic button\n * in controlled mode, so every toggle looks identical.\n */\nimport { useThemeSetting } from '@hanzogui/next-theme'\nimport { AgnosticThemeToggle, type ThemeToggleProps } from './ThemeToggle'\n\nfunction ThemeToggleNext({ size, label }: Pick<ThemeToggleProps, 'size' | 'label'>) {\n const { current, resolvedTheme, set } = useThemeSetting()\n const isDark = (resolvedTheme ?? current ?? 'dark') !== 'light'\n return (\n <AgnosticThemeToggle\n theme={isDark ? 'dark' : 'light'}\n onToggle={(next) => set(next)}\n size={size}\n label={label}\n />\n )\n}\n\nexport default ThemeToggleNext\nexport { ThemeToggleNext }\n"]}
@@ -1,6 +1,7 @@
1
1
  'use client';
2
2
  'use strict';
3
3
 
4
+ var chunk57YEM34R_cjs = require('./chunk-57YEM34R.cjs');
4
5
  var chunkMWBFAV3D_cjs = require('./chunk-MWBFAV3D.cjs');
5
6
  var react = require('react');
6
7
  var gui = require('@hanzo/gui');
@@ -93,6 +94,7 @@ function DataTable({
93
94
  sort,
94
95
  onSortChange
95
96
  }) {
97
+ const track = chunk57YEM34R_cjs.useEmit();
96
98
  const minTableW = columns.reduce((sum, c) => sum + (c.width ?? FLEX_MIN_COL_W), 0) + (columns.length + 1) * 12;
97
99
  return /* @__PURE__ */ jsxRuntime.jsxs(gui.YStack, { borderWidth: 1, borderColor: "$borderColor", rounded: "$4", overflow: "hidden", children: [
98
100
  /* @__PURE__ */ jsxRuntime.jsx(gui.YStack, { style: { overflowX: "auto", overflowY: "visible" }, children: /* @__PURE__ */ jsxRuntime.jsxs(gui.YStack, { minW: minTableW, children: [
@@ -114,7 +116,10 @@ function DataTable({
114
116
  "aria-label": sortable ? `Sort by ${c.header}` : void 0,
115
117
  cursor: sortable ? "pointer" : void 0,
116
118
  hoverStyle: sortable ? { opacity: 0.75 } : void 0,
117
- onPress: sortable ? () => onSortChange?.(c.key) : void 0,
119
+ onPress: sortable ? () => {
120
+ track({ component: "DataTable", action: "sort", id: c.key, value: sort?.key === c.key && sort?.dir === "asc" ? "desc" : "asc" });
121
+ onSortChange?.(c.key);
122
+ } : void 0,
118
123
  children: [
119
124
  /* @__PURE__ */ jsxRuntime.jsx(
120
125
  gui.Text,
@@ -149,7 +154,10 @@ function DataTable({
149
154
  bg: expanded ? "$color2" : void 0,
150
155
  hoverStyle: onRowPress ? { bg: "$color2" } : void 0,
151
156
  cursor: onRowPress ? "pointer" : void 0,
152
- onPress: onRowPress ? () => onRowPress(row) : void 0,
157
+ onPress: onRowPress ? () => {
158
+ track({ component: "DataTable", action: "select", id: rowKey(row), value: rows.length });
159
+ onRowPress(row);
160
+ } : void 0,
153
161
  children: columns.map((c) => {
154
162
  const cell = c.render ? c.render(row) : String(row[c.key] ?? "");
155
163
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -236,6 +244,12 @@ function useAccent() {
236
244
  }
237
245
  function PrimaryButton(props) {
238
246
  const { accent, contrast } = useAccent();
247
+ const track = chunk57YEM34R_cjs.useEmit();
248
+ const onPress = (e) => {
249
+ track({ component: "PrimaryButton", action: "click", id: typeof props.children === "string" ? props.children : props["aria-label"] });
250
+ props.onPress?.(e);
251
+ };
252
+ props = { ...props, onPress };
239
253
  if (accent) {
240
254
  return /* @__PURE__ */ jsxRuntime.jsx(gui.Button, { style: { backgroundColor: accent, color: contrast, borderColor: accent }, ...props });
241
255
  }
@@ -252,6 +266,7 @@ function EmptyState({
252
266
  primary,
253
267
  secondary
254
268
  }) {
269
+ const track = chunk57YEM34R_cjs.useEmit();
255
270
  return /* @__PURE__ */ jsxRuntime.jsxs(
256
271
  gui.Card,
257
272
  {
@@ -291,7 +306,11 @@ function EmptyState({
291
306
  {
292
307
  icon: primary.icon,
293
308
  iconAfter: primary.href ? /* @__PURE__ */ jsxRuntime.jsx(lucideIcons2.ExternalLink, { size: 15 }) : /* @__PURE__ */ jsxRuntime.jsx(lucideIcons2.ArrowRight, { size: 15 }),
294
- onPress: () => primary.href ? openHref(primary.href) : primary.onPress?.(),
309
+ onPress: () => {
310
+ track({ component: "EmptyState", action: "click", id: title, value: primary.label });
311
+ if (primary.href) openHref(primary.href);
312
+ else primary.onPress?.();
313
+ },
295
314
  children: primary.label
296
315
  }
297
316
  ) : null,
@@ -301,7 +320,11 @@ function EmptyState({
301
320
  chromeless: true,
302
321
  icon: secondary.icon,
303
322
  iconAfter: secondary.href ? /* @__PURE__ */ jsxRuntime.jsx(lucideIcons2.ExternalLink, { size: 14 }) : void 0,
304
- onPress: () => secondary.href ? openHref(secondary.href) : secondary.onPress?.(),
323
+ onPress: () => {
324
+ track({ component: "EmptyState", action: "click", id: title, value: secondary.label });
325
+ if (secondary.href) openHref(secondary.href);
326
+ else secondary.onPress?.();
327
+ },
305
328
  children: secondary.label
306
329
  }
307
330
  ) : null
@@ -310,11 +333,17 @@ function EmptyState({
310
333
  }
311
334
  );
312
335
  }
336
+ var FieldNameContext = react.createContext(void 0);
337
+ function useFieldTrack(component) {
338
+ const track = chunk57YEM34R_cjs.useEmit();
339
+ const name = react.useContext(FieldNameContext);
340
+ return (e) => track({ component, id: name, ...e });
341
+ }
313
342
  function FieldRow({ label, children }) {
314
- return /* @__PURE__ */ jsxRuntime.jsxs(gui.XStack, { gap: "$3", items: "flex-start", flexWrap: "wrap", children: [
343
+ return /* @__PURE__ */ jsxRuntime.jsx(FieldNameContext.Provider, { value: label, children: /* @__PURE__ */ jsxRuntime.jsxs(gui.XStack, { gap: "$3", items: "flex-start", flexWrap: "wrap", children: [
315
344
  /* @__PURE__ */ jsxRuntime.jsx(gui.Label, { width: "100%", pt: "$2", color: "$color11", fontSize: "$3", $md: { width: 180 }, children: label }),
316
345
  /* @__PURE__ */ jsxRuntime.jsx(gui.YStack, { flex: 1, minW: 0, $md: { minW: 240 }, children })
317
- ] });
346
+ ] }) });
318
347
  }
319
348
  function FieldText({
320
349
  value,
@@ -323,11 +352,15 @@ function FieldText({
323
352
  secure,
324
353
  placeholder
325
354
  }) {
355
+ const track = useFieldTrack("FieldText");
326
356
  return /* @__PURE__ */ jsxRuntime.jsx(
327
357
  gui.Input,
328
358
  {
329
359
  value,
330
- onChangeText: onChange,
360
+ onChangeText: (v) => {
361
+ track({ action: "change", value: secure ? 0 : chunk57YEM34R_cjs.textSize(v) });
362
+ onChange(v);
363
+ },
331
364
  disabled,
332
365
  secureTextEntry: secure,
333
366
  placeholder,
@@ -341,14 +374,39 @@ function FieldTextArea({
341
374
  disabled,
342
375
  rows = 6
343
376
  }) {
344
- return /* @__PURE__ */ jsxRuntime.jsx(gui.TextArea, { value, onChangeText: onChange, disabled, numberOfLines: rows });
377
+ const track = useFieldTrack("FieldTextArea");
378
+ return /* @__PURE__ */ jsxRuntime.jsx(
379
+ gui.TextArea,
380
+ {
381
+ value,
382
+ onChangeText: (v) => {
383
+ track({ action: "change", value: chunk57YEM34R_cjs.textSize(v) });
384
+ onChange(v);
385
+ },
386
+ disabled,
387
+ numberOfLines: rows
388
+ }
389
+ );
345
390
  }
346
391
  function FieldSwitch({
347
392
  checked,
348
393
  onChange,
349
394
  disabled
350
395
  }) {
351
- return /* @__PURE__ */ jsxRuntime.jsx(gui.Switch, { checked, onCheckedChange: onChange, disabled, size: "$3", children: /* @__PURE__ */ jsxRuntime.jsx(gui.Switch.Thumb, {}) });
396
+ const track = useFieldTrack("FieldSwitch");
397
+ return /* @__PURE__ */ jsxRuntime.jsx(
398
+ gui.Switch,
399
+ {
400
+ checked,
401
+ onCheckedChange: (v) => {
402
+ track({ action: "change", value: v });
403
+ onChange(v);
404
+ },
405
+ disabled,
406
+ size: "$3",
407
+ children: /* @__PURE__ */ jsxRuntime.jsx(gui.Switch.Thumb, {})
408
+ }
409
+ );
352
410
  }
353
411
  var CHEVRON = `data:image/svg+xml,${encodeURIComponent(
354
412
  '<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#9a9a9a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6"/></svg>'
@@ -382,12 +440,16 @@ function FieldSelect({
382
440
  disabled,
383
441
  placeholder = "Select\u2026"
384
442
  }) {
443
+ const track = useFieldTrack("FieldSelect");
385
444
  const showPlaceholder = value === "" || !options.includes(value);
386
445
  return /* @__PURE__ */ jsxRuntime.jsxs(
387
446
  "select",
388
447
  {
389
448
  value: showPlaceholder ? "" : value,
390
- onChange: (e) => onChange(e.currentTarget.value),
449
+ onChange: (e) => {
450
+ track({ action: "change", value: e.currentTarget.value });
451
+ onChange(e.currentTarget.value);
452
+ },
391
453
  disabled,
392
454
  "aria-label": placeholder,
393
455
  style: selectStyle(disabled),
@@ -406,6 +468,7 @@ function FieldSlider({
406
468
  onChange,
407
469
  disabled
408
470
  }) {
471
+ const track = useFieldTrack("FieldSlider");
409
472
  return /* @__PURE__ */ jsxRuntime.jsxs(gui.XStack, { gap: "$3", items: "center", children: [
410
473
  /* @__PURE__ */ jsxRuntime.jsx(gui.Text, { width: 56, fontSize: "$3", color: "$color11", children: value }),
411
474
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -416,7 +479,10 @@ function FieldSlider({
416
479
  max,
417
480
  step,
418
481
  value: [value],
419
- onValueChange: (v) => onChange(v[0] ?? min),
482
+ onValueChange: (v) => {
483
+ track({ action: "change", value: v[0] ?? min });
484
+ onChange(v[0] ?? min);
485
+ },
420
486
  disabled,
421
487
  children: [
422
488
  /* @__PURE__ */ jsxRuntime.jsx(gui.Slider.Track, { children: /* @__PURE__ */ jsxRuntime.jsx(gui.Slider.TrackActive, {}) }),
@@ -504,8 +570,12 @@ function SlideOver({
504
570
  zIndex = 1e3,
505
571
  children
506
572
  }) {
573
+ const track = chunk57YEM34R_cjs.useEmit();
507
574
  const panelRef = react.useRef(null);
508
575
  const openerRef = react.useRef(null);
576
+ react.useEffect(() => {
577
+ track({ component: "SlideOver", action: open ? "open" : "close", id: typeof title === "string" ? title : ariaLabel });
578
+ }, [open]);
509
579
  react.useEffect(() => {
510
580
  if (!open) return;
511
581
  openerRef.current = typeof document !== "undefined" ? document.activeElement : null;
@@ -1178,5 +1248,5 @@ exports.resolveAccent = resolveAccent;
1178
1248
  exports.setOrgAccent = setOrgAccent;
1179
1249
  exports.useAccent = useAccent;
1180
1250
  exports.useHost = useHost;
1181
- //# sourceMappingURL=chunk-5MBAHVEO.cjs.map
1182
- //# sourceMappingURL=chunk-5MBAHVEO.cjs.map
1251
+ //# sourceMappingURL=chunk-XULZJEW2.cjs.map
1252
+ //# sourceMappingURL=chunk-XULZJEW2.cjs.map