@adea-ai/ui 0.138.1 → 0.139.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.
@@ -8,6 +8,12 @@ import type { ComponentProps } from 'solid-js';
8
8
  * narrow screens the middle track yields space to the controls, and each side
9
9
  * group scrolls when necessary so keyboard focus can reveal every action.
10
10
  *
11
+ * The centre track is sized for a lone search field. A bar whose centre holds
12
+ * more than that, such as a search plus an action beside it, passes
13
+ * `centerWidth="wide"`: the centre then stays fluid below 1280px, never
14
+ * narrower than 7rem so its controls are not squeezed out by a long leading
15
+ * group (which scrolls instead), and takes a wider fixed track above it.
16
+ *
11
17
  * On a frameless desktop window this row is also the drag region, so it carries
12
18
  * `window-drag` and the controls inside it opt back out. `macos-inset` and
13
19
  * `windows-inset` reserve the space the native window controls occupy, which is
@@ -23,6 +29,13 @@ export type TopBarProps = ComponentProps<'header'> & {
23
29
  windowsInset?: boolean;
24
30
  /** Blur the content scrolling under the bar. */
25
31
  glass?: boolean;
32
+ /**
33
+ * How wide the centre track is. `field` (default) suits a lone search field.
34
+ * `wide` suits a centre that also holds an action: fluid below 1280px and
35
+ * never narrower than 7rem (the side groups scroll to make room), then a
36
+ * clamp of 32vw up to 672px between equal side tracks.
37
+ */
38
+ centerWidth?: 'field' | 'wide';
26
39
  };
27
40
  export declare function TopBar(props: TopBarProps): import("solid-js").JSX.Element;
28
41
  /** A scrollable side group; equal wide-screen tracks centre the search. */
@@ -1 +1 @@
1
- {"version":3,"file":"top-bar.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/top-bar/top-bar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAK9C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG;IACnD,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,qDAAqD;IACrD,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB,CAAA;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,kCA2BxC;AAYD,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;CAAE,kCAqBvF;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7B,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAC3B,kCAgBF;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,kCAY5D;AAED,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG;IACzD,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kCA8BpD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,kCAqB7E"}
1
+ {"version":3,"file":"top-bar.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/top-bar/top-bar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAK9C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG;IACnD,sDAAsD;IACtD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kDAAkD;IAClD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,qDAAqD;IACrD,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAA;IACf;;;;;OAKG;IACH,WAAW,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;CAC/B,CAAA;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,kCA8BxC;AAYD,2EAA2E;AAC3E,wBAAgB,aAAa,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,OAAO,GAAG,KAAK,CAAA;CAAE,kCAqBvF;AAED,wBAAgB,WAAW,CACzB,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,GAAG;IAC7B,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAC3B,kCAgBF;AAED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,CAAC,KAAK,CAAC,kCAY5D;AAED,MAAM,MAAM,iBAAiB,GAAG,cAAc,CAAC,QAAQ,CAAC,GAAG;IACzD,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,kCA8BpD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,cAAc,CAAC,QAAQ,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,kCAqB7E"}
@@ -16,12 +16,13 @@ function TopBar(props) {
16
16
  "draggable",
17
17
  "macosInset",
18
18
  "windowsInset",
19
- "glass"
19
+ "glass",
20
+ "centerWidth"
20
21
  ]);
21
22
  return (() => {
22
23
  var _el$ = _tmpl$();
23
24
  spread(_el$, mergeProps({ get ["class"]() {
24
- return cn("h-topbar relative z-(--z-sticky) grid shrink-0 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3 border-b border-border bg-card text-card-foreground md:grid-cols-[minmax(0,1fr)_clamp(240px,22vw,480px)_minmax(0,1fr)]", local.glass && "glass-panel", local.draggable && "window-drag", local.macosInset ? "window-inset-macos" : "ps-3", local.windowsInset ? "window-inset-windows" : "pe-3", local.class);
25
+ return cn("h-topbar relative z-(--z-sticky) grid shrink-0 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3 border-b border-border bg-card text-card-foreground md:grid-cols-[minmax(0,1fr)_clamp(240px,22vw,480px)_minmax(0,1fr)]", local.centerWidth === "wide" && "grid-cols-[auto_minmax(7rem,1fr)_auto] gap-2 md:grid-cols-[auto_minmax(7rem,1fr)_auto] xl:grid-cols-[minmax(0,1fr)_clamp(240px,32vw,672px)_minmax(0,1fr)]", local.glass && "glass-panel", local.draggable && "window-drag", local.macosInset ? "window-inset-macos" : "ps-3", local.windowsInset ? "window-inset-windows" : "pe-3", local.class);
25
26
  } }, rest), false, false);
26
27
  return _el$;
27
28
  })();
@@ -1 +1 @@
1
- {"version":3,"file":"top-bar.js","names":["ComponentProps","Show","splitProps","Search","cn","TopBarProps","draggable","macosInset","windowsInset","glass","TopBar","props","local","rest","_el$","_tmpl$","_$spread","_$mergeProps","class","invokeFocusHandler","handler","event","Event","Array","isArray","callback","data","TopBarSection","align","_el$2","_tmpl$2","$$focusin","target","HTMLElement","scrollIntoView","block","inline","onFocusIn","TopBarTitle","_el$3","_tmpl$3","TopBarBreadcrumb","_el$4","_tmpl$4","TopBarSearchProps","placeholder","shortcut","keyshortcuts","TopBarSearch","_el$5","_tmpl$6","_el$6","firstChild","aria-keyshortcuts","_$insert","_$createComponent","when","children","_el$7","_tmpl$5","TopBarPill","label","_el$8","_tmpl$7","_el$9","_$delegateEvents","_$mergeProps","_$createComponent","_$delegateEvents"],"sources":["../../../../src/components/layout/top-bar/top-bar.tsx"],"sourcesContent":["import type { ComponentProps } from 'solid-js'\nimport { Show, splitProps } from 'solid-js'\nimport { Search } from 'lucide-solid'\nimport { cn } from '#lib/utils'\n\n/**\n * TopBar.\n *\n * The window's title row and primary toolbar. Three tracks: a leading group, a\n * centred search, and a trailing group. On wide screens the search is a fixed\n * function of the window width and the side groups are equal remainders. On\n * narrow screens the middle track yields space to the controls, and each side\n * group scrolls when necessary so keyboard focus can reveal every action.\n *\n * On a frameless desktop window this row is also the drag region, so it carries\n * `window-drag` and the controls inside it opt back out. `macos-inset` and\n * `windows-inset` reserve the space the native window controls occupy, which is\n * why the row can look off-centre in a frameless window and be correct: it is\n * centred in the space the user can actually see.\n */\nexport type TopBarProps = ComponentProps<'header'> & {\n /** Make the row draggable, for a frameless window. */\n draggable?: boolean\n /** Reserve space for the macOS traffic lights. */\n macosInset?: boolean\n /** Reserve space for the Windows caption buttons. */\n windowsInset?: boolean\n /** Blur the content scrolling under the bar. */\n glass?: boolean\n}\n\nexport function TopBar(props: TopBarProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'draggable',\n 'macosInset',\n 'windowsInset',\n 'glass',\n ])\n\n return (\n <header\n data-slot=\"top-bar\"\n class={cn(\n 'h-topbar relative z-(--z-sticky) grid shrink-0 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3 border-b border-border bg-card text-card-foreground md:grid-cols-[minmax(0,1fr)_clamp(240px,22vw,480px)_minmax(0,1fr)]',\n local.glass && 'glass-panel',\n local.draggable && 'window-drag',\n /* Each edge gets exactly one inline-padding utility. A window inset\n and px-3 would share a layer and tie-break by build order, and a\n build that put px-3 last hid the leading controls under the\n traffic lights. */\n local.macosInset ? 'window-inset-macos' : 'ps-3',\n local.windowsInset ? 'window-inset-windows' : 'pe-3',\n local.class\n )}\n {...rest}\n />\n )\n}\n\nfunction invokeFocusHandler(handler: unknown, event: Event) {\n if (Array.isArray(handler)) {\n const [callback, data] = handler as [(data: unknown, event: Event) => void, unknown]\n callback(data, event)\n } else if (typeof handler === 'function') {\n const callback = handler as (event: Event) => void\n callback(event)\n }\n}\n\n/** A scrollable side group; equal wide-screen tracks centre the search. */\nexport function TopBarSection(props: ComponentProps<'div'> & { align?: 'start' | 'end' }) {\n const [local, rest] = splitProps(props, ['class', 'align', 'onFocusIn'])\n\n return (\n <div\n data-slot=\"top-bar-section\"\n class={cn(\n 'window-no-drag flex min-w-0 items-center gap-2 overflow-x-auto overflow-y-hidden',\n local.align === 'end' && 'md:justify-end',\n local.align !== 'end' && 'justify-start',\n local.class\n )}\n onFocusIn={(event) => {\n if (event.target instanceof HTMLElement) {\n event.target.scrollIntoView({ block: 'nearest', inline: 'nearest' })\n }\n invokeFocusHandler(local.onFocusIn, event)\n }}\n {...rest}\n />\n )\n}\n\nexport function TopBarTitle(\n props: ComponentProps<'div'> & {\n /** Aligns text within this slot; it does not position the slot. */\n align?: 'start' | 'center'\n }\n) {\n const [local, rest] = splitProps(props, ['class', 'align'])\n return (\n <div\n data-slot=\"top-bar-title\"\n class={cn(\n /* The title slot sits in the drag region; a link, button or field\n placed in it (a breadcrumb, a view's search) must stay pressable. */\n 'truncate text-sm font-medium [&_:is(a,button,input)]:window-no-drag',\n local.align === 'center' && 'text-center',\n local.class\n )}\n {...rest}\n />\n )\n}\n\n/** A breadcrumb or path readout that yields to the actions when space runs out. */\nexport function TopBarBreadcrumb(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"top-bar-breadcrumb\"\n class={cn(\n 'flex min-w-0 items-center gap-1.5 text-sm [&_:is(a,button,input)]:window-no-drag',\n local.class\n )}\n {...rest}\n />\n )\n}\n\nexport type TopBarSearchProps = ComponentProps<'button'> & {\n /** The hint shown inside the field, e.g. \"Search projects and files\". */\n placeholder?: string\n /** The shortcut chip drawn at the trailing edge, e.g. \"⌘K\". */\n shortcut?: string\n /**\n * The parseable chord behind `shortcut`, e.g. `Meta+K`. The chip is\n * `aria-hidden` — reading it out would pollute the button's accessible\n * name — so this is what assistive technology announces instead. Supply it\n * whenever `shortcut` is drawn.\n */\n keyshortcuts?: string\n}\n\n/**\n * The search affordance in the middle track.\n *\n * It is a button, not an input, and that is deliberate: clicking it opens the\n * command palette, which already owns the input, the ranking and the keyboard\n * handling. A second live input here would be a second source of truth for the\n * same query. It is styled to read as a field because that is what it behaves\n * like from the user's side.\n */\nexport function TopBarSearch(props: TopBarSearchProps) {\n const [local, rest] = splitProps(props, ['class', 'placeholder', 'shortcut', 'keyshortcuts'])\n\n return (\n <button\n type=\"button\"\n data-slot=\"top-bar-search\"\n class={cn(\n 'text-muted-foreground flex h-control-md w-full min-w-(--control-height-md) items-center justify-center gap-2 rounded-md border border-input px-3 text-sm sm:justify-start',\n 'transition-colors ease-out outline-none',\n 'hover:bg-surface-hover hover:text-foreground',\n 'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',\n 'window-no-drag',\n local.class\n )}\n aria-keyshortcuts={local.keyshortcuts}\n {...rest}\n >\n <Search aria-hidden=\"true\" class=\"size-4 shrink-0 sm:hidden\" />\n <span class=\"sr-only sm:not-sr-only sm:truncate\">{local.placeholder ?? 'Search'}</span>\n <Show when={local.shortcut}>\n <kbd\n aria-hidden=\"true\"\n class=\"bg-muted ms-auto hidden h-5 shrink-0 items-center rounded-sm border border-border px-1.5 font-code text-code sm:flex\"\n >\n {local.shortcut}\n </kbd>\n </Show>\n </button>\n )\n}\n\n/**\n * A labelled top-bar control — the readouts KiroCrew puts in the trailing\n * group. Container queries on the group decide which of these survive a narrow\n * window; a caller marks the cheap-to-drop ones and the group handles the rest.\n */\nexport function TopBarPill(props: ComponentProps<'button'> & { label: string }) {\n const [local, rest] = splitProps(props, ['class', 'label', 'children'])\n\n return (\n <button\n type=\"button\"\n data-slot=\"top-bar-pill\"\n class={cn(\n 'text-muted-foreground inline-flex h-control-sm shrink-0 items-center gap-1.5 rounded-md border border-border px-2.5 text-xs font-medium',\n 'transition-colors ease-out outline-none',\n 'hover:border-input hover:text-foreground',\n 'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',\n 'window-no-drag [&_svg]:size-3.5 [&_svg]:shrink-0',\n local.class\n )}\n {...rest}\n >\n {local.children}\n <span>{local.label}</span>\n </button>\n )\n}\n"],"mappings":";;;;;;;;;;;;AA+BA,SAAgBU,OAAOC,OAAoB;CACzC,MAAM,CAACC,OAAOC,QAAQX,WAAWS,OAAO;EACtC;EACA;EACA;EACA;EACA;CAAO,CACR;CAED,cAAA;EAAA,IAAAG,OAAAC,OAAA;EAAAC,OAAAF,MAAAG,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWb,GACL,yOACAQ,MAAMH,SAAS,eACfG,MAAMN,aAAa,eAKnBM,MAAML,aAAa,uBAAuB,QAC1CK,MAAMJ,eAAe,yBAAyB,QAC9CI,MAAMM,KACR;EAAC,EAAA,GACGL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAC;CAAA,EAAA,CAAA;AAGd;AAEA,SAASK,mBAAmBC,SAAkBC,OAAc;CAC1D,IAAIE,MAAMC,QAAQJ,OAAO,GAAG;EAC1B,MAAM,CAACK,UAAUC,QAAQN;EACzBK,SAASC,MAAML,KAAK;CACtB,OAAO,IAAI,OAAOD,YAAY,YAE5BK,QAASJ,KAAK;AAElB;;AAGA,SAAgBM,cAAchB,OAA4D;CACxF,MAAM,CAACC,OAAOC,QAAQX,WAAWS,OAAO;EAAC;EAAS;EAAS;CAAW,CAAC;CAEvE,cAAA;EAAA,IAAAkB,QAAAC,QAAA;EAAAD,MAAAE,aASgBV,UAAU;GACpB,IAAIA,MAAMW,kBAAkBC,aAC1BZ,MAAMW,OAAOE,eAAe;IAAEC,OAAO;IAAWC,QAAQ;GAAU,CAAC;GAErEjB,mBAAmBP,MAAMyB,WAAWhB,KAAK;EAC3C;EAACL,OAAAa,OAAAZ,WAAA,EAAA,KAAA,WAAA;GAAA,OAXMb,GACL,oFACAQ,MAAMgB,UAAU,SAAS,kBACzBhB,MAAMgB,UAAU,SAAS,iBACzBhB,MAAMM,KACR;EAAC,EAAA,GAOGL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAgB;CAAA,EAAA,CAAA;AAGd;AAEA,SAAgBS,YACd3B,OAIA;CACA,MAAM,CAACC,OAAOC,QAAQX,WAAWS,OAAO,CAAC,SAAS,OAAO,CAAC;CAC1D,cAAA;EAAA,IAAA4B,QAAAC,QAAA;EAAAxB,OAAAuB,OAAAtB,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWb,GAGL,uEACAQ,MAAMgB,UAAU,YAAY,eAC5BhB,MAAMM,KACR;EAAC,EAAA,GACGL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAA0B;CAAA,EAAA,CAAA;AAGd;;AAGA,SAAgBE,iBAAiB9B,OAA8B;CAC7D,MAAM,CAACC,OAAOC,QAAQX,WAAWS,OAAO,CAAC,OAAO,CAAC;CACjD,cAAA;EAAA,IAAA+B,QAAAC,QAAA;EAAA3B,OAAA0B,OAAAzB,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWb,GACL,oFACAQ,MAAMM,KACR;EAAC,EAAA,GACGL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAA6B;CAAA,EAAA,CAAA;AAGd;;;;;;;;;;AAyBA,SAAgBM,aAAarC,OAA0B;CACrD,MAAM,CAACC,OAAOC,QAAQX,WAAWS,OAAO;EAAC;EAAS;EAAe;EAAY;CAAc,CAAC;CAE5F,cAAA;EAAA,IAAAsC,QAAAC,QAAA,GAAAC,QAAAF,MAAAG;EAAApC,OAAAiC,OAAAhC,WAAA;GAAA,KAAA,WAAA;IAAA,OAIWb,GACL,6KACA,2CACA,gDACA,oFACA,kBACAQ,MAAMM,KACR;GAAC;GAAA,KAAA,uBAAA;IAAA,OACkBN,MAAMmC;GAAY;EAAA,GACjClC,IAAI,GAAA,OAAA,IAAA;EAAAyC,OAAAL,OAAAM,gBAEPpD,QAAM;GAAA,eAAA;GAAA,SAAA;EAAA,CAAA,GAAAgD,KAAA;EAAAG,OAAAH,aAC2CvC,MAAMiC,eAAe,QAAQ;EAAAS,OAAAL,OAAAM,gBAC9EtD,MAAI;GAAA,IAACuD,OAAI;IAAA,OAAE5C,MAAMkC;GAAQ;GAAA,IAAAW,WAAA;IAAA,IAAAC,QAAAC,QAAA;IAAAL,OAAAI,aAKrB9C,MAAMkC,QAAQ;IAAA,OAAAY;GAAA;EAAA,CAAA,GAAA,IAAA;EAAA,OAAAT;CAAA,EAAA,CAAA;AAKzB;;;;;;AAOA,SAAgBW,WAAWjD,OAAqD;CAC9E,MAAM,CAACC,OAAOC,QAAQX,WAAWS,OAAO;EAAC;EAAS;EAAS;CAAU,CAAC;CAEtE,cAAA;EAAA,IAAAmD,QAAAC,QAAA,GAAAC,QAAAF,MAAAV;EAAApC,OAAA8C,OAAA7C,WAAA,EAAA,KAAA,WAAA;GAAA,OAIWb,GACL,2IACA,2CACA,4CACA,oFACA,oDACAQ,MAAMM,KACR;EAAC,EAAA,GACGL,IAAI,GAAA,OAAA,IAAA;EAAAyC,OAAAQ,aAEPlD,MAAM6C,UAAQO,KAAA;EAAAV,OAAAU,aACRpD,MAAMiD,KAAK;EAAA,OAAAC;CAAA,EAAA,CAAA;AAGxB;AAACG,eAAA,CAAA,SAAA,CAAA"}
1
+ {"version":3,"file":"top-bar.js","names":["ComponentProps","Show","splitProps","Search","cn","TopBarProps","draggable","macosInset","windowsInset","glass","centerWidth","TopBar","props","local","rest","_el$","_tmpl$","_$spread","_$mergeProps","class","invokeFocusHandler","handler","event","Event","Array","isArray","callback","data","TopBarSection","align","_el$2","_tmpl$2","$$focusin","target","HTMLElement","scrollIntoView","block","inline","onFocusIn","TopBarTitle","_el$3","_tmpl$3","TopBarBreadcrumb","_el$4","_tmpl$4","TopBarSearchProps","placeholder","shortcut","keyshortcuts","TopBarSearch","_el$5","_tmpl$6","_el$6","firstChild","aria-keyshortcuts","_$insert","_$createComponent","when","children","_el$7","_tmpl$5","TopBarPill","label","_el$8","_tmpl$7","_el$9","_$delegateEvents","_$mergeProps","_$createComponent","_$delegateEvents"],"sources":["../../../../src/components/layout/top-bar/top-bar.tsx"],"sourcesContent":["import type { ComponentProps } from 'solid-js'\nimport { Show, splitProps } from 'solid-js'\nimport { Search } from 'lucide-solid'\nimport { cn } from '#lib/utils'\n\n/**\n * TopBar.\n *\n * The window's title row and primary toolbar. Three tracks: a leading group, a\n * centred search, and a trailing group. On wide screens the search is a fixed\n * function of the window width and the side groups are equal remainders. On\n * narrow screens the middle track yields space to the controls, and each side\n * group scrolls when necessary so keyboard focus can reveal every action.\n *\n * The centre track is sized for a lone search field. A bar whose centre holds\n * more than that, such as a search plus an action beside it, passes\n * `centerWidth=\"wide\"`: the centre then stays fluid below 1280px, never\n * narrower than 7rem so its controls are not squeezed out by a long leading\n * group (which scrolls instead), and takes a wider fixed track above it.\n *\n * On a frameless desktop window this row is also the drag region, so it carries\n * `window-drag` and the controls inside it opt back out. `macos-inset` and\n * `windows-inset` reserve the space the native window controls occupy, which is\n * why the row can look off-centre in a frameless window and be correct: it is\n * centred in the space the user can actually see.\n */\nexport type TopBarProps = ComponentProps<'header'> & {\n /** Make the row draggable, for a frameless window. */\n draggable?: boolean\n /** Reserve space for the macOS traffic lights. */\n macosInset?: boolean\n /** Reserve space for the Windows caption buttons. */\n windowsInset?: boolean\n /** Blur the content scrolling under the bar. */\n glass?: boolean\n /**\n * How wide the centre track is. `field` (default) suits a lone search field.\n * `wide` suits a centre that also holds an action: fluid below 1280px and\n * never narrower than 7rem (the side groups scroll to make room), then a\n * clamp of 32vw up to 672px between equal side tracks.\n */\n centerWidth?: 'field' | 'wide'\n}\n\nexport function TopBar(props: TopBarProps) {\n const [local, rest] = splitProps(props, [\n 'class',\n 'draggable',\n 'macosInset',\n 'windowsInset',\n 'glass',\n 'centerWidth',\n ])\n\n return (\n <header\n data-slot=\"top-bar\"\n class={cn(\n 'h-topbar relative z-(--z-sticky) grid shrink-0 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3 border-b border-border bg-card text-card-foreground md:grid-cols-[minmax(0,1fr)_clamp(240px,22vw,480px)_minmax(0,1fr)]',\n local.centerWidth === 'wide' &&\n 'grid-cols-[auto_minmax(7rem,1fr)_auto] gap-2 md:grid-cols-[auto_minmax(7rem,1fr)_auto] xl:grid-cols-[minmax(0,1fr)_clamp(240px,32vw,672px)_minmax(0,1fr)]',\n local.glass && 'glass-panel',\n local.draggable && 'window-drag',\n /* Each edge gets exactly one inline-padding utility. A window inset\n and px-3 would share a layer and tie-break by build order, and a\n build that put px-3 last hid the leading controls under the\n traffic lights. */\n local.macosInset ? 'window-inset-macos' : 'ps-3',\n local.windowsInset ? 'window-inset-windows' : 'pe-3',\n local.class\n )}\n {...rest}\n />\n )\n}\n\nfunction invokeFocusHandler(handler: unknown, event: Event) {\n if (Array.isArray(handler)) {\n const [callback, data] = handler as [(data: unknown, event: Event) => void, unknown]\n callback(data, event)\n } else if (typeof handler === 'function') {\n const callback = handler as (event: Event) => void\n callback(event)\n }\n}\n\n/** A scrollable side group; equal wide-screen tracks centre the search. */\nexport function TopBarSection(props: ComponentProps<'div'> & { align?: 'start' | 'end' }) {\n const [local, rest] = splitProps(props, ['class', 'align', 'onFocusIn'])\n\n return (\n <div\n data-slot=\"top-bar-section\"\n class={cn(\n 'window-no-drag flex min-w-0 items-center gap-2 overflow-x-auto overflow-y-hidden',\n local.align === 'end' && 'md:justify-end',\n local.align !== 'end' && 'justify-start',\n local.class\n )}\n onFocusIn={(event) => {\n if (event.target instanceof HTMLElement) {\n event.target.scrollIntoView({ block: 'nearest', inline: 'nearest' })\n }\n invokeFocusHandler(local.onFocusIn, event)\n }}\n {...rest}\n />\n )\n}\n\nexport function TopBarTitle(\n props: ComponentProps<'div'> & {\n /** Aligns text within this slot; it does not position the slot. */\n align?: 'start' | 'center'\n }\n) {\n const [local, rest] = splitProps(props, ['class', 'align'])\n return (\n <div\n data-slot=\"top-bar-title\"\n class={cn(\n /* The title slot sits in the drag region; a link, button or field\n placed in it (a breadcrumb, a view's search) must stay pressable. */\n 'truncate text-sm font-medium [&_:is(a,button,input)]:window-no-drag',\n local.align === 'center' && 'text-center',\n local.class\n )}\n {...rest}\n />\n )\n}\n\n/** A breadcrumb or path readout that yields to the actions when space runs out. */\nexport function TopBarBreadcrumb(props: ComponentProps<'div'>) {\n const [local, rest] = splitProps(props, ['class'])\n return (\n <div\n data-slot=\"top-bar-breadcrumb\"\n class={cn(\n 'flex min-w-0 items-center gap-1.5 text-sm [&_:is(a,button,input)]:window-no-drag',\n local.class\n )}\n {...rest}\n />\n )\n}\n\nexport type TopBarSearchProps = ComponentProps<'button'> & {\n /** The hint shown inside the field, e.g. \"Search projects and files\". */\n placeholder?: string\n /** The shortcut chip drawn at the trailing edge, e.g. \"⌘K\". */\n shortcut?: string\n /**\n * The parseable chord behind `shortcut`, e.g. `Meta+K`. The chip is\n * `aria-hidden` — reading it out would pollute the button's accessible\n * name — so this is what assistive technology announces instead. Supply it\n * whenever `shortcut` is drawn.\n */\n keyshortcuts?: string\n}\n\n/**\n * The search affordance in the middle track.\n *\n * It is a button, not an input, and that is deliberate: clicking it opens the\n * command palette, which already owns the input, the ranking and the keyboard\n * handling. A second live input here would be a second source of truth for the\n * same query. It is styled to read as a field because that is what it behaves\n * like from the user's side.\n */\nexport function TopBarSearch(props: TopBarSearchProps) {\n const [local, rest] = splitProps(props, ['class', 'placeholder', 'shortcut', 'keyshortcuts'])\n\n return (\n <button\n type=\"button\"\n data-slot=\"top-bar-search\"\n class={cn(\n 'text-muted-foreground flex h-control-md w-full min-w-(--control-height-md) items-center justify-center gap-2 rounded-md border border-input px-3 text-sm sm:justify-start',\n 'transition-colors ease-out outline-none',\n 'hover:bg-surface-hover hover:text-foreground',\n 'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',\n 'window-no-drag',\n local.class\n )}\n aria-keyshortcuts={local.keyshortcuts}\n {...rest}\n >\n <Search aria-hidden=\"true\" class=\"size-4 shrink-0 sm:hidden\" />\n <span class=\"sr-only sm:not-sr-only sm:truncate\">{local.placeholder ?? 'Search'}</span>\n <Show when={local.shortcut}>\n <kbd\n aria-hidden=\"true\"\n class=\"bg-muted ms-auto hidden h-5 shrink-0 items-center rounded-sm border border-border px-1.5 font-code text-code sm:flex\"\n >\n {local.shortcut}\n </kbd>\n </Show>\n </button>\n )\n}\n\n/**\n * A labelled top-bar control — the readouts KiroCrew puts in the trailing\n * group. Container queries on the group decide which of these survive a narrow\n * window; a caller marks the cheap-to-drop ones and the group handles the rest.\n */\nexport function TopBarPill(props: ComponentProps<'button'> & { label: string }) {\n const [local, rest] = splitProps(props, ['class', 'label', 'children'])\n\n return (\n <button\n type=\"button\"\n data-slot=\"top-bar-pill\"\n class={cn(\n 'text-muted-foreground inline-flex h-control-sm shrink-0 items-center gap-1.5 rounded-md border border-border px-2.5 text-xs font-medium',\n 'transition-colors ease-out outline-none',\n 'hover:border-input hover:text-foreground',\n 'focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-primary-subtle',\n 'window-no-drag [&_svg]:size-3.5 [&_svg]:shrink-0',\n local.class\n )}\n {...rest}\n >\n {local.children}\n <span>{local.label}</span>\n </button>\n )\n}\n"],"mappings":";;;;;;;;;;;;AA4CA,SAAgBW,OAAOC,OAAoB;CACzC,MAAM,CAACC,OAAOC,QAAQZ,WAAWU,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;CAAa,CACd;CAED,cAAA;EAAA,IAAAG,OAAAC,OAAA;EAAAC,OAAAF,MAAAG,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWd,GACL,yOACAS,MAAMH,gBAAgB,UACpB,6JACFG,MAAMJ,SAAS,eACfI,MAAMP,aAAa,eAKnBO,MAAMN,aAAa,uBAAuB,QAC1CM,MAAML,eAAe,yBAAyB,QAC9CK,MAAMM,KACR;EAAC,EAAA,GACGL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAC;CAAA,EAAA,CAAA;AAGd;AAEA,SAASK,mBAAmBC,SAAkBC,OAAc;CAC1D,IAAIE,MAAMC,QAAQJ,OAAO,GAAG;EAC1B,MAAM,CAACK,UAAUC,QAAQN;EACzBK,SAASC,MAAML,KAAK;CACtB,OAAO,IAAI,OAAOD,YAAY,YAE5BK,QAASJ,KAAK;AAElB;;AAGA,SAAgBM,cAAchB,OAA4D;CACxF,MAAM,CAACC,OAAOC,QAAQZ,WAAWU,OAAO;EAAC;EAAS;EAAS;CAAW,CAAC;CAEvE,cAAA;EAAA,IAAAkB,QAAAC,QAAA;EAAAD,MAAAE,aASgBV,UAAU;GACpB,IAAIA,MAAMW,kBAAkBC,aAC1BZ,MAAMW,OAAOE,eAAe;IAAEC,OAAO;IAAWC,QAAQ;GAAU,CAAC;GAErEjB,mBAAmBP,MAAMyB,WAAWhB,KAAK;EAC3C;EAACL,OAAAa,OAAAZ,WAAA,EAAA,KAAA,WAAA;GAAA,OAXMd,GACL,oFACAS,MAAMgB,UAAU,SAAS,kBACzBhB,MAAMgB,UAAU,SAAS,iBACzBhB,MAAMM,KACR;EAAC,EAAA,GAOGL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAAgB;CAAA,EAAA,CAAA;AAGd;AAEA,SAAgBS,YACd3B,OAIA;CACA,MAAM,CAACC,OAAOC,QAAQZ,WAAWU,OAAO,CAAC,SAAS,OAAO,CAAC;CAC1D,cAAA;EAAA,IAAA4B,QAAAC,QAAA;EAAAxB,OAAAuB,OAAAtB,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWd,GAGL,uEACAS,MAAMgB,UAAU,YAAY,eAC5BhB,MAAMM,KACR;EAAC,EAAA,GACGL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAA0B;CAAA,EAAA,CAAA;AAGd;;AAGA,SAAgBE,iBAAiB9B,OAA8B;CAC7D,MAAM,CAACC,OAAOC,QAAQZ,WAAWU,OAAO,CAAC,OAAO,CAAC;CACjD,cAAA;EAAA,IAAA+B,QAAAC,QAAA;EAAA3B,OAAA0B,OAAAzB,WAAA,EAAA,KAAA,WAAA;GAAA,OAGWd,GACL,oFACAS,MAAMM,KACR;EAAC,EAAA,GACGL,IAAI,GAAA,OAAA,KAAA;EAAA,OAAA6B;CAAA,EAAA,CAAA;AAGd;;;;;;;;;;AAyBA,SAAgBM,aAAarC,OAA0B;CACrD,MAAM,CAACC,OAAOC,QAAQZ,WAAWU,OAAO;EAAC;EAAS;EAAe;EAAY;CAAc,CAAC;CAE5F,cAAA;EAAA,IAAAsC,QAAAC,QAAA,GAAAC,QAAAF,MAAAG;EAAApC,OAAAiC,OAAAhC,WAAA;GAAA,KAAA,WAAA;IAAA,OAIWd,GACL,6KACA,2CACA,gDACA,oFACA,kBACAS,MAAMM,KACR;GAAC;GAAA,KAAA,uBAAA;IAAA,OACkBN,MAAMmC;GAAY;EAAA,GACjClC,IAAI,GAAA,OAAA,IAAA;EAAAyC,OAAAL,OAAAM,gBAEPrD,QAAM;GAAA,eAAA;GAAA,SAAA;EAAA,CAAA,GAAAiD,KAAA;EAAAG,OAAAH,aAC2CvC,MAAMiC,eAAe,QAAQ;EAAAS,OAAAL,OAAAM,gBAC9EvD,MAAI;GAAA,IAACwD,OAAI;IAAA,OAAE5C,MAAMkC;GAAQ;GAAA,IAAAW,WAAA;IAAA,IAAAC,QAAAC,QAAA;IAAAL,OAAAI,aAKrB9C,MAAMkC,QAAQ;IAAA,OAAAY;GAAA;EAAA,CAAA,GAAA,IAAA;EAAA,OAAAT;CAAA,EAAA,CAAA;AAKzB;;;;;;AAOA,SAAgBW,WAAWjD,OAAqD;CAC9E,MAAM,CAACC,OAAOC,QAAQZ,WAAWU,OAAO;EAAC;EAAS;EAAS;CAAU,CAAC;CAEtE,cAAA;EAAA,IAAAmD,QAAAC,QAAA,GAAAC,QAAAF,MAAAV;EAAApC,OAAA8C,OAAA7C,WAAA,EAAA,KAAA,WAAA;GAAA,OAIWd,GACL,2IACA,2CACA,4CACA,oFACA,oDACAS,MAAMM,KACR;EAAC,EAAA,GACGL,IAAI,GAAA,OAAA,IAAA;EAAAyC,OAAAQ,aAEPlD,MAAM6C,UAAQO,KAAA;EAAAV,OAAAU,aACRpD,MAAMiD,KAAK;EAAA,OAAAC;CAAA,EAAA,CAAA;AAGxB;AAACG,eAAA,CAAA,SAAA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"secret-input-group.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/input-group/secret-input-group.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA0B,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAKhF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAA;IACnD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,IAAI,GAAG,YAAY,GAAG,kBAAkB,GAAG,cAAc,CAAC,CAAA;AAE5F;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,eAiD5D"}
1
+ {"version":3,"file":"secret-input-group.d.ts","sourceRoot":"","sources":["../../../../src/components/ui/input-group/secret-input-group.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAgC,KAAK,cAAc,EAAE,KAAK,GAAG,EAAE,MAAM,UAAU,CAAA;AAKtF,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,GAAG,CAAC,YAAY,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAA;IACnD,OAAO,CAAC,EAAE,MAAM,IAAI,CAAA;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GAAG,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,IAAI,GAAG,YAAY,GAAG,kBAAkB,GAAG,cAAc,CAAC,CAAA;AAE5F;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,eAoD5D"}
@@ -2,7 +2,7 @@ import { FormFieldContext } from "../../../lib/form-field.js";
2
2
  import { ActionButton } from "../../composites/action-button/action-button.js";
3
3
  import { InputGroup, InputGroupInput } from "./input-group.js";
4
4
  import { createComponent, memo, mergeProps } from "solid-js/web";
5
- import { splitProps, useContext } from "solid-js";
5
+ import { Show, splitProps, useContext } from "solid-js";
6
6
  //#region src/components/ui/input-group/secret-input-group.tsx
7
7
  /**
8
8
  * A password input with an optional keyboard-accessible clear action.
@@ -40,34 +40,35 @@ function SecretInputGroup(props) {
40
40
  return local.onChange;
41
41
  }
42
42
  }));
43
- if (!local.onClear) return createComponent(InputGroup, {
44
- get ["class"]() {
45
- return local.class;
46
- },
47
- children: input
48
- });
49
43
  return createComponent(InputGroup, {
50
44
  get ["class"]() {
51
45
  return local.class;
52
46
  },
53
47
  get children() {
54
- return [input, createComponent(ActionButton, {
55
- "data-slot": "input-group-button",
56
- variant: "ghost",
57
- size: "xs",
58
- "class": "-me-1 h-6 min-w-0 shrink-0 px-2",
59
- get disabled() {
60
- return local.disabled;
61
- },
62
- get tooltip() {
63
- return memo(() => !!local.disabled)() ? local.disabledReason ?? "This API key is managed by policy" : local.clearLabel ?? "Clear API key";
64
- },
65
- tooltipSide: "top",
66
- get ["aria-label"]() {
67
- return local.clearLabel ?? "Clear API key";
48
+ return [input, createComponent(Show, {
49
+ get when() {
50
+ return local.onClear;
68
51
  },
69
- onClick: () => local.onClear?.(),
70
- children: "Clear"
52
+ get children() {
53
+ return createComponent(ActionButton, {
54
+ "data-slot": "input-group-button",
55
+ variant: "ghost",
56
+ size: "xs",
57
+ "class": "-me-1 h-6 min-w-0 shrink-0 px-2",
58
+ get disabled() {
59
+ return local.disabled;
60
+ },
61
+ get tooltip() {
62
+ return memo(() => !!local.disabled)() ? local.disabledReason ?? "This API key is managed by policy" : local.clearLabel ?? "Clear API key";
63
+ },
64
+ tooltipSide: "top",
65
+ get ["aria-label"]() {
66
+ return local.clearLabel ?? "Clear API key";
67
+ },
68
+ onClick: () => local.onClear?.(),
69
+ children: "Clear"
70
+ });
71
+ }
71
72
  })];
72
73
  }
73
74
  });
@@ -1 +1 @@
1
- {"version":3,"file":"secret-input-group.js","names":["splitProps","useContext","ComponentProps","JSX","ActionButton","FormFieldContext","InputGroup","InputGroupInput","SecretInputGroupProps","value","disabled","onChange","EventHandler","HTMLInputElement","Event","onClear","clearLabel","disabledReason","class","Pick","SecretInputGroup","props","field","local","rest","input","_$createComponent","_$mergeProps","aria-label","undefined","type","autocomplete","onInput","children","variant","size","tooltip","_$memo","tooltipSide","onClick","_$createComponent","_$mergeProps","_$memo"],"sources":["../../../../src/components/ui/input-group/secret-input-group.tsx"],"sourcesContent":["import { splitProps, useContext, type ComponentProps, type JSX } from 'solid-js'\nimport { ActionButton } from '../../composites/action-button'\nimport { FormFieldContext } from '#lib/form-field'\nimport { InputGroup, InputGroupInput } from './input-group'\n\nexport type SecretInputGroupProps = {\n value: string\n disabled: boolean\n onChange: JSX.EventHandler<HTMLInputElement, Event>\n onClear?: () => void\n /**\n * What the clear action does, for this field: its accessible name and its\n * tooltip while enabled (\"Clear stored token\", \"Clear Google token path\").\n * Defaults to \"Clear API key\".\n */\n clearLabel?: string\n /**\n * Why the clear action is unavailable, shown as its tooltip while the group is\n * `disabled`. Defaults to the policy message, so name the real reason when a\n * field is disabled for another one (\"Set a token variable first\").\n */\n disabledReason?: string\n class?: string\n} & Pick<ComponentProps<'input'>, 'id' | 'aria-label' | 'aria-describedby' | 'aria-invalid'>\n\n/**\n * A password input with an optional keyboard-accessible clear action.\n *\n * The wording is the caller's: inside a `FormField` the input takes the field's\n * label, outside one it needs `aria-label` (the fallback names an API key), and\n * `clearLabel` and `disabledReason` name the clear action for the field it\n * belongs to, so a bearer token or a token path is not announced as an API key.\n */\nexport function SecretInputGroup(props: SecretInputGroupProps) {\n const field = useContext(FormFieldContext)\n const [local, rest] = splitProps(props, [\n 'value',\n 'disabled',\n 'onChange',\n 'onClear',\n 'clearLabel',\n 'disabledReason',\n 'aria-label',\n 'class',\n ])\n\n const input = (\n <InputGroupInput\n {...rest}\n aria-label={local['aria-label'] ?? (field ? undefined : 'New API key')}\n type=\"password\"\n autocomplete=\"new-password\"\n value={local.value}\n disabled={local.disabled}\n onInput={local.onChange}\n />\n )\n\n if (!local.onClear) return <InputGroup class={local.class}>{input}</InputGroup>\n\n return (\n <InputGroup class={local.class}>\n {input}\n <ActionButton\n data-slot=\"input-group-button\"\n variant=\"ghost\"\n size=\"xs\"\n class=\"-me-1 h-6 min-w-0 shrink-0 px-2\"\n disabled={local.disabled}\n tooltip={\n local.disabled\n ? (local.disabledReason ?? 'This API key is managed by policy')\n : (local.clearLabel ?? 'Clear API key')\n }\n tooltipSide=\"top\"\n aria-label={local.clearLabel ?? 'Clear API key'}\n onClick={() => local.onClear?.()}\n >\n Clear\n </ActionButton>\n </InputGroup>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAiCA,SAAgBoB,iBAAiBC,OAA8B;CAC7D,MAAMC,QAAQrB,WAAWI,gBAAgB;CACzC,MAAM,CAACkB,OAAOC,QAAQxB,WAAWqB,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAO,CACR;CAED,MAAMI,QAAKC,gBACRnB,iBAAeoB,WACVH,MAAI;EAAA,KAAA,gBAAA;GAAA,OACID,MAAM,kBAAkBD,QAAQO,KAAAA,IAAY;EAAc;EACtEC,MAAI;EACJC,cAAY;EAAA,IACZtB,QAAK;GAAA,OAAEc,MAAMd;EAAK;EAAA,IAClBC,WAAQ;GAAA,OAAEa,MAAMb;EAAQ;EAAA,IACxBsB,UAAO;GAAA,OAAET,MAAMZ;EAAQ;CAAA,CAAA,CAAA;CAI3B,IAAI,CAACY,MAAMR,SAAS,OAAAW,gBAAQpB,YAAU;EAAA,KAAA,WAAA;GAAA,OAAQiB,MAAML;EAAK;EAAAe,UAAGR;CAAK,CAAA;CAEjE,OAAAC,gBACGpB,YAAU;EAAA,KAAA,WAAA;GAAA,OAAQiB,MAAML;EAAK;EAAA,IAAAe,WAAA;GAAA,OAAA,CAC3BR,OAAKC,gBACLtB,cAAY;IAAA,aAAA;IAEX8B,SAAO;IACPC,MAAI;IAAA,SAAA;IAAA,IAEJzB,WAAQ;KAAA,OAAEa,MAAMb;IAAQ;IAAA,IACxB0B,UAAO;KAAA,OACLC,WAAA,CAAA,CAAAd,MAAMb,QAAQ,CAAA,CAAA,IACTa,MAAMN,kBAAkB,sCACxBM,MAAMP,cAAc;IAAgB;IAE3CsB,aAAW;IAAA,KAAA,gBAAA;KAAA,OACCf,MAAMP,cAAc;IAAe;IAC/CuB,eAAehB,MAAMR,UAAU;IAACkB,UAAA;GAAA,CAAA,CAAA;EAAA;CAAA,CAAA;AAMxC"}
1
+ {"version":3,"file":"secret-input-group.js","names":["Show","splitProps","useContext","ComponentProps","JSX","ActionButton","FormFieldContext","InputGroup","InputGroupInput","SecretInputGroupProps","value","disabled","onChange","EventHandler","HTMLInputElement","Event","onClear","clearLabel","disabledReason","class","Pick","SecretInputGroup","props","field","local","rest","input","_$createComponent","_$mergeProps","aria-label","undefined","type","autocomplete","onInput","children","when","variant","size","tooltip","_$memo","tooltipSide","onClick","_$createComponent","_$mergeProps","_$memo"],"sources":["../../../../src/components/ui/input-group/secret-input-group.tsx"],"sourcesContent":["import { Show, splitProps, useContext, type ComponentProps, type JSX } from 'solid-js'\nimport { ActionButton } from '../../composites/action-button'\nimport { FormFieldContext } from '#lib/form-field'\nimport { InputGroup, InputGroupInput } from './input-group'\n\nexport type SecretInputGroupProps = {\n value: string\n disabled: boolean\n onChange: JSX.EventHandler<HTMLInputElement, Event>\n onClear?: () => void\n /**\n * What the clear action does, for this field: its accessible name and its\n * tooltip while enabled (\"Clear stored token\", \"Clear Google token path\").\n * Defaults to \"Clear API key\".\n */\n clearLabel?: string\n /**\n * Why the clear action is unavailable, shown as its tooltip while the group is\n * `disabled`. Defaults to the policy message, so name the real reason when a\n * field is disabled for another one (\"Set a token variable first\").\n */\n disabledReason?: string\n class?: string\n} & Pick<ComponentProps<'input'>, 'id' | 'aria-label' | 'aria-describedby' | 'aria-invalid'>\n\n/**\n * A password input with an optional keyboard-accessible clear action.\n *\n * The wording is the caller's: inside a `FormField` the input takes the field's\n * label, outside one it needs `aria-label` (the fallback names an API key), and\n * `clearLabel` and `disabledReason` name the clear action for the field it\n * belongs to, so a bearer token or a token path is not announced as an API key.\n */\nexport function SecretInputGroup(props: SecretInputGroupProps) {\n const field = useContext(FormFieldContext)\n const [local, rest] = splitProps(props, [\n 'value',\n 'disabled',\n 'onChange',\n 'onClear',\n 'clearLabel',\n 'disabledReason',\n 'aria-label',\n 'class',\n ])\n\n const input = (\n <InputGroupInput\n {...rest}\n aria-label={local['aria-label'] ?? (field ? undefined : 'New API key')}\n type=\"password\"\n autocomplete=\"new-password\"\n value={local.value}\n disabled={local.disabled}\n onInput={local.onChange}\n />\n )\n\n // One group for both cases: `onClear` may appear or disappear after setup\n // (a stored secret is cleared, or a configured one arrives), and the input\n // must keep its element, value and focus while the action comes and goes.\n return (\n <InputGroup class={local.class}>\n {input}\n <Show when={local.onClear}>\n <ActionButton\n data-slot=\"input-group-button\"\n variant=\"ghost\"\n size=\"xs\"\n class=\"-me-1 h-6 min-w-0 shrink-0 px-2\"\n disabled={local.disabled}\n tooltip={\n local.disabled\n ? (local.disabledReason ?? 'This API key is managed by policy')\n : (local.clearLabel ?? 'Clear API key')\n }\n tooltipSide=\"top\"\n aria-label={local.clearLabel ?? 'Clear API key'}\n onClick={() => local.onClear?.()}\n >\n Clear\n </ActionButton>\n </Show>\n </InputGroup>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;AAiCA,SAAgBqB,iBAAiBC,OAA8B;CAC7D,MAAMC,QAAQrB,WAAWI,gBAAgB;CACzC,MAAM,CAACkB,OAAOC,QAAQxB,WAAWqB,OAAO;EACtC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CAAO,CACR;CAED,MAAMI,QAAKC,gBACRnB,iBAAeoB,WACVH,MAAI;EAAA,KAAA,gBAAA;GAAA,OACID,MAAM,kBAAkBD,QAAQO,KAAAA,IAAY;EAAc;EACtEC,MAAI;EACJC,cAAY;EAAA,IACZtB,QAAK;GAAA,OAAEc,MAAMd;EAAK;EAAA,IAClBC,WAAQ;GAAA,OAAEa,MAAMb;EAAQ;EAAA,IACxBsB,UAAO;GAAA,OAAET,MAAMZ;EAAQ;CAAA,CAAA,CAAA;CAO3B,OAAAe,gBACGpB,YAAU;EAAA,KAAA,WAAA;GAAA,OAAQiB,MAAML;EAAK;EAAA,IAAAe,WAAA;GAAA,OAAA,CAC3BR,OAAKC,gBACL3B,MAAI;IAAA,IAACmC,OAAI;KAAA,OAAEX,MAAMR;IAAO;IAAA,IAAAkB,WAAA;KAAA,OAAAP,gBACtBtB,cAAY;MAAA,aAAA;MAEX+B,SAAO;MACPC,MAAI;MAAA,SAAA;MAAA,IAEJ1B,WAAQ;OAAA,OAAEa,MAAMb;MAAQ;MAAA,IACxB2B,UAAO;OAAA,OACLC,WAAA,CAAA,CAAAf,MAAMb,QAAQ,CAAA,CAAA,IACTa,MAAMN,kBAAkB,sCACxBM,MAAMP,cAAc;MAAgB;MAE3CuB,aAAW;MAAA,KAAA,gBAAA;OAAA,OACChB,MAAMP,cAAc;MAAe;MAC/CwB,eAAejB,MAAMR,UAAU;MAACkB,UAAA;KAAA,CAAA;IAAA;GAAA,CAAA,CAAA;EAAA;CAAA,CAAA;AAO1C"}
@@ -12,6 +12,12 @@ import { cn } from '../../../lib/utils'
12
12
  * narrow screens the middle track yields space to the controls, and each side
13
13
  * group scrolls when necessary so keyboard focus can reveal every action.
14
14
  *
15
+ * The centre track is sized for a lone search field. A bar whose centre holds
16
+ * more than that, such as a search plus an action beside it, passes
17
+ * `centerWidth="wide"`: the centre then stays fluid below 1280px, never
18
+ * narrower than 7rem so its controls are not squeezed out by a long leading
19
+ * group (which scrolls instead), and takes a wider fixed track above it.
20
+ *
15
21
  * On a frameless desktop window this row is also the drag region, so it carries
16
22
  * `window-drag` and the controls inside it opt back out. `macos-inset` and
17
23
  * `windows-inset` reserve the space the native window controls occupy, which is
@@ -27,6 +33,13 @@ export type TopBarProps = ComponentProps<'header'> & {
27
33
  windowsInset?: boolean
28
34
  /** Blur the content scrolling under the bar. */
29
35
  glass?: boolean
36
+ /**
37
+ * How wide the centre track is. `field` (default) suits a lone search field.
38
+ * `wide` suits a centre that also holds an action: fluid below 1280px and
39
+ * never narrower than 7rem (the side groups scroll to make room), then a
40
+ * clamp of 32vw up to 672px between equal side tracks.
41
+ */
42
+ centerWidth?: 'field' | 'wide'
30
43
  }
31
44
 
32
45
  export function TopBar(props: TopBarProps) {
@@ -36,6 +49,7 @@ export function TopBar(props: TopBarProps) {
36
49
  'macosInset',
37
50
  'windowsInset',
38
51
  'glass',
52
+ 'centerWidth',
39
53
  ])
40
54
 
41
55
  return (
@@ -43,6 +57,8 @@ export function TopBar(props: TopBarProps) {
43
57
  data-slot="top-bar"
44
58
  class={cn(
45
59
  'h-topbar relative z-(--z-sticky) grid shrink-0 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3 border-b border-border bg-card text-card-foreground md:grid-cols-[minmax(0,1fr)_clamp(240px,22vw,480px)_minmax(0,1fr)]',
60
+ local.centerWidth === 'wide' &&
61
+ 'grid-cols-[auto_minmax(7rem,1fr)_auto] gap-2 md:grid-cols-[auto_minmax(7rem,1fr)_auto] xl:grid-cols-[minmax(0,1fr)_clamp(240px,32vw,672px)_minmax(0,1fr)]',
46
62
  local.glass && 'glass-panel',
47
63
  local.draggable && 'window-drag',
48
64
  /* Each edge gets exactly one inline-padding utility. A window inset
@@ -1,4 +1,4 @@
1
- import { splitProps, useContext, type ComponentProps, type JSX } from 'solid-js'
1
+ import { Show, splitProps, useContext, type ComponentProps, type JSX } from 'solid-js'
2
2
  import { ActionButton } from '../../composites/action-button'
3
3
  import { FormFieldContext } from '../../../lib/form-field'
4
4
  import { InputGroup, InputGroupInput } from './input-group'
@@ -56,28 +56,31 @@ export function SecretInputGroup(props: SecretInputGroupProps) {
56
56
  />
57
57
  )
58
58
 
59
- if (!local.onClear) return <InputGroup class={local.class}>{input}</InputGroup>
60
-
59
+ // One group for both cases: `onClear` may appear or disappear after setup
60
+ // (a stored secret is cleared, or a configured one arrives), and the input
61
+ // must keep its element, value and focus while the action comes and goes.
61
62
  return (
62
63
  <InputGroup class={local.class}>
63
64
  {input}
64
- <ActionButton
65
- data-slot="input-group-button"
66
- variant="ghost"
67
- size="xs"
68
- class="-me-1 h-6 min-w-0 shrink-0 px-2"
69
- disabled={local.disabled}
70
- tooltip={
71
- local.disabled
72
- ? (local.disabledReason ?? 'This API key is managed by policy')
73
- : (local.clearLabel ?? 'Clear API key')
74
- }
75
- tooltipSide="top"
76
- aria-label={local.clearLabel ?? 'Clear API key'}
77
- onClick={() => local.onClear?.()}
78
- >
79
- Clear
80
- </ActionButton>
65
+ <Show when={local.onClear}>
66
+ <ActionButton
67
+ data-slot="input-group-button"
68
+ variant="ghost"
69
+ size="xs"
70
+ class="-me-1 h-6 min-w-0 shrink-0 px-2"
71
+ disabled={local.disabled}
72
+ tooltip={
73
+ local.disabled
74
+ ? (local.disabledReason ?? 'This API key is managed by policy')
75
+ : (local.clearLabel ?? 'Clear API key')
76
+ }
77
+ tooltipSide="top"
78
+ aria-label={local.clearLabel ?? 'Clear API key'}
79
+ onClick={() => local.onClear?.()}
80
+ >
81
+ Clear
82
+ </ActionButton>
83
+ </Show>
81
84
  </InputGroup>
82
85
  )
83
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.138.1",
3
+ "version": "0.139.0",
4
4
  "description": "The Adea design system: Kobalte-backed Solid components, semantic OKLCH tokens, and the app-shell layout primitives shared by Adea and Cortana.",
5
5
  "keywords": [
6
6
  "adea",
@@ -12,6 +12,12 @@ import { cn } from '#lib/utils'
12
12
  * narrow screens the middle track yields space to the controls, and each side
13
13
  * group scrolls when necessary so keyboard focus can reveal every action.
14
14
  *
15
+ * The centre track is sized for a lone search field. A bar whose centre holds
16
+ * more than that, such as a search plus an action beside it, passes
17
+ * `centerWidth="wide"`: the centre then stays fluid below 1280px, never
18
+ * narrower than 7rem so its controls are not squeezed out by a long leading
19
+ * group (which scrolls instead), and takes a wider fixed track above it.
20
+ *
15
21
  * On a frameless desktop window this row is also the drag region, so it carries
16
22
  * `window-drag` and the controls inside it opt back out. `macos-inset` and
17
23
  * `windows-inset` reserve the space the native window controls occupy, which is
@@ -27,6 +33,13 @@ export type TopBarProps = ComponentProps<'header'> & {
27
33
  windowsInset?: boolean
28
34
  /** Blur the content scrolling under the bar. */
29
35
  glass?: boolean
36
+ /**
37
+ * How wide the centre track is. `field` (default) suits a lone search field.
38
+ * `wide` suits a centre that also holds an action: fluid below 1280px and
39
+ * never narrower than 7rem (the side groups scroll to make room), then a
40
+ * clamp of 32vw up to 672px between equal side tracks.
41
+ */
42
+ centerWidth?: 'field' | 'wide'
30
43
  }
31
44
 
32
45
  export function TopBar(props: TopBarProps) {
@@ -36,6 +49,7 @@ export function TopBar(props: TopBarProps) {
36
49
  'macosInset',
37
50
  'windowsInset',
38
51
  'glass',
52
+ 'centerWidth',
39
53
  ])
40
54
 
41
55
  return (
@@ -43,6 +57,8 @@ export function TopBar(props: TopBarProps) {
43
57
  data-slot="top-bar"
44
58
  class={cn(
45
59
  'h-topbar relative z-(--z-sticky) grid shrink-0 grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] items-center gap-3 border-b border-border bg-card text-card-foreground md:grid-cols-[minmax(0,1fr)_clamp(240px,22vw,480px)_minmax(0,1fr)]',
60
+ local.centerWidth === 'wide' &&
61
+ 'grid-cols-[auto_minmax(7rem,1fr)_auto] gap-2 md:grid-cols-[auto_minmax(7rem,1fr)_auto] xl:grid-cols-[minmax(0,1fr)_clamp(240px,32vw,672px)_minmax(0,1fr)]',
46
62
  local.glass && 'glass-panel',
47
63
  local.draggable && 'window-drag',
48
64
  /* Each edge gets exactly one inline-padding utility. A window inset
@@ -1,4 +1,4 @@
1
- import { splitProps, useContext, type ComponentProps, type JSX } from 'solid-js'
1
+ import { Show, splitProps, useContext, type ComponentProps, type JSX } from 'solid-js'
2
2
  import { ActionButton } from '../../composites/action-button'
3
3
  import { FormFieldContext } from '#lib/form-field'
4
4
  import { InputGroup, InputGroupInput } from './input-group'
@@ -56,28 +56,31 @@ export function SecretInputGroup(props: SecretInputGroupProps) {
56
56
  />
57
57
  )
58
58
 
59
- if (!local.onClear) return <InputGroup class={local.class}>{input}</InputGroup>
60
-
59
+ // One group for both cases: `onClear` may appear or disappear after setup
60
+ // (a stored secret is cleared, or a configured one arrives), and the input
61
+ // must keep its element, value and focus while the action comes and goes.
61
62
  return (
62
63
  <InputGroup class={local.class}>
63
64
  {input}
64
- <ActionButton
65
- data-slot="input-group-button"
66
- variant="ghost"
67
- size="xs"
68
- class="-me-1 h-6 min-w-0 shrink-0 px-2"
69
- disabled={local.disabled}
70
- tooltip={
71
- local.disabled
72
- ? (local.disabledReason ?? 'This API key is managed by policy')
73
- : (local.clearLabel ?? 'Clear API key')
74
- }
75
- tooltipSide="top"
76
- aria-label={local.clearLabel ?? 'Clear API key'}
77
- onClick={() => local.onClear?.()}
78
- >
79
- Clear
80
- </ActionButton>
65
+ <Show when={local.onClear}>
66
+ <ActionButton
67
+ data-slot="input-group-button"
68
+ variant="ghost"
69
+ size="xs"
70
+ class="-me-1 h-6 min-w-0 shrink-0 px-2"
71
+ disabled={local.disabled}
72
+ tooltip={
73
+ local.disabled
74
+ ? (local.disabledReason ?? 'This API key is managed by policy')
75
+ : (local.clearLabel ?? 'Clear API key')
76
+ }
77
+ tooltipSide="top"
78
+ aria-label={local.clearLabel ?? 'Clear API key'}
79
+ onClick={() => local.onClear?.()}
80
+ >
81
+ Clear
82
+ </ActionButton>
83
+ </Show>
81
84
  </InputGroup>
82
85
  )
83
86
  }