@kitn.ai/ui 0.18.0 → 0.18.2
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.
- package/dist/elements/agent-card.js +1 -1
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/avatar.js +1 -1
- package/dist/elements/badge.js +1 -1
- package/dist/elements/button.js +1 -1
- package/dist/elements/card.js +1 -1
- package/dist/elements/cards.js +1 -1
- package/dist/elements/chain-of-thought.js +1 -1
- package/dist/elements/chat-scope-picker.js +1 -1
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/chunks/{Icon-DLLBeLG5.js → Icon-C2LTALdM.js} +1 -1
- package/dist/elements/chunks/{action-icons-UB4V5FNq.js → action-icons-Dtl8Ox5D.js} +1 -1
- package/dist/elements/chunks/arrow-left-GbBceGBE.js +1 -0
- package/dist/elements/chunks/{attachments-CgbErf1r.js → attachments-DLJcWCDV.js} +1 -1
- package/dist/elements/chunks/{badge-DTQpgB6N.js → badge-BTNZTODL.js} +1 -1
- package/dist/elements/chunks/{button-XDUM6lXs.js → button-DBDZebGr.js} +1 -1
- package/dist/elements/chunks/{card-CBlFZ-7I.js → card-YfCyI4fH.js} +1 -1
- package/dist/elements/chunks/{chat-thread-BIhmqwNL.js → chat-thread-Dz2qbzvI.js} +1 -1
- package/dist/elements/chunks/check-fhmRksq_.js +1 -0
- package/dist/elements/chunks/chevron-down-DcRg1aRU.js +1 -0
- package/dist/elements/chunks/chevron-right-ChzBcNJ-.js +1 -0
- package/dist/elements/chunks/circle-B4fTe9v6.js +1 -0
- package/dist/elements/chunks/circle-check-CJV-3Rem.js +1 -0
- package/dist/elements/chunks/{circle-x-DewgAEym.js → circle-x-pDtzlghQ.js} +1 -1
- package/dist/elements/chunks/{code-block-DedwxwFI.js → code-block-DZ6mz6vs.js} +1 -1
- package/dist/elements/chunks/{collapsible-FfgmTHtM.js → collapsible-CWUAShMC.js} +1 -1
- package/dist/elements/chunks/{composer-Cfbw5HKT.js → composer-BZyPU6gD.js} +1 -1
- package/dist/elements/chunks/{context-DwJmxlRE.js → context-Cx6eQA_G.js} +1 -1
- package/dist/elements/chunks/{conversation-list-DsCQMfPq.js → conversation-list-C4xTQSpT.js} +1 -1
- package/dist/elements/chunks/{default-input-BuqsoENC.js → default-input-BZcwyKcP.js} +1 -1
- package/dist/elements/chunks/define-CNSKFZCd.js +1 -0
- package/dist/elements/chunks/{disclosure-CwoAkpMH.js → disclosure-BcBs--Jf.js} +1 -1
- package/dist/elements/chunks/{download-DJ06G3Bz.js → download-BDQwcFav.js} +1 -1
- package/dist/elements/chunks/{dropdown-BFg8ri_R.js → dropdown-wgs8xCJZ.js} +1 -1
- package/dist/elements/chunks/{ellipsis-dlN6YvGw.js → ellipsis-CUiEJ6R1.js} +1 -1
- package/dist/elements/chunks/{external-link-DBX2lc_R.js → external-link-DJUmC7g2.js} +1 -1
- package/dist/elements/chunks/{file-text-HfwUx4F9.js → file-text-DOYfYvrh.js} +1 -1
- package/dist/elements/chunks/{file-tree-C6Toj71u.js → file-tree-DQzNQrbr.js} +1 -1
- package/dist/elements/chunks/{folder-MYuqa9Xp.js → folder-DBDUBANz.js} +1 -1
- package/dist/elements/chunks/{hover-card-gE7iyXdW.js → hover-card-BQGdGAQ3.js} +1 -1
- package/dist/elements/chunks/{icon-Vqz4ivrY.js → icon-CAyRqVrg.js} +1 -1
- package/dist/elements/chunks/{info-B_toFS0l.js → info-DBQl-m6e.js} +1 -1
- package/dist/elements/chunks/{input-gzbHYZvQ.js → input-DZD67EvW.js} +1 -1
- package/dist/elements/chunks/{kbd-n49c4KZm.js → kbd-DbyVrlnU.js} +1 -1
- package/dist/elements/chunks/{link-CKPs9yM0.js → link-vko7AttA.js} +1 -1
- package/dist/elements/chunks/{loader-q6MamGus.js → loader-Cg6rO8tQ.js} +1 -1
- package/dist/elements/chunks/{markdown-DBrcqq4S.js → markdown-n3Xbj3PR.js} +1 -1
- package/dist/elements/chunks/{message-CTqaYI0s.js → message-CgKytiSB.js} +1 -1
- package/dist/elements/chunks/{message-feedback-Cvzm03w0.js → message-feedback-Ds3-US0e.js} +1 -1
- package/dist/elements/chunks/message-square-MmRkvc-M.js +1 -0
- package/dist/elements/chunks/{minimize-2-l2klxP6K.js → minimize-2-3kCdE0q3.js} +1 -1
- package/dist/elements/chunks/{model-switcher-CmyjiHO1.js → model-switcher-BMtXGwLU.js} +1 -1
- package/dist/elements/chunks/{overlay-C8sOYqjT.js → overlay-B1-MBI6h.js} +1 -1
- package/dist/elements/chunks/{paperclip-42Ia7xHD.js → paperclip-DDEfmkHc.js} +1 -1
- package/dist/elements/chunks/{progress-bar-tzKMWAwO.js → progress-bar-Dwbiyv3s.js} +1 -1
- package/dist/elements/chunks/{prompt-suggestion-D3F6vP_A.js → prompt-suggestion-9lvgFcEa.js} +1 -1
- package/dist/elements/chunks/{reasoning-BJNmmyov.js → reasoning-DzHWsdAJ.js} +1 -1
- package/dist/elements/chunks/{resizable-BRsk19qI.js → resizable-Bq_eHxUm.js} +1 -1
- package/dist/elements/chunks/{rotate-cw-BgcggLHC.js → rotate-cw-jpisOdOV.js} +1 -1
- package/dist/elements/chunks/{scroll-area-CgGbFFbT.js → scroll-area-g5Ze3K6f.js} +1 -1
- package/dist/elements/chunks/{separator-8hM4GdHE.js → separator-Bmjc5XyX.js} +1 -1
- package/dist/elements/chunks/{settings-CaUIYOg-.js → settings-Cz1bMJP9.js} +1 -1
- package/dist/elements/chunks/{settings-group-CYjOVIZQ.js → settings-group-9imY3kRA.js} +1 -1
- package/dist/elements/chunks/{share-BTYM3Ddt.js → share-BdenF0wQ.js} +1 -1
- package/dist/elements/chunks/{skeleton-DsTYrPUe.js → skeleton-Pjc5jiaY.js} +1 -1
- package/dist/elements/chunks/{star-9R3LBQ9n.js → star-DY6z1UUH.js} +1 -1
- package/dist/elements/chunks/{store-CYl5Y0_u.js → store-th4VyJXM.js} +1 -1
- package/dist/elements/chunks/{text-shimmer-CaYGv8Se.js → text-shimmer-CEi-tlXr.js} +1 -1
- package/dist/elements/chunks/{textarea-NEr2SLcD.js → textarea-CboigarR.js} +1 -1
- package/dist/elements/chunks/{thumbs-up-DZYZkFfc.js → thumbs-up-BR33KLfA.js} +1 -1
- package/dist/elements/chunks/{toast-store-ClxVZRcc.js → toast-store-BEMda0fv.js} +1 -1
- package/dist/elements/chunks/{tool-CHZOmoe9.js → tool-TMued3WO.js} +1 -1
- package/dist/elements/chunks/{tooltip-WpHElELC.js → tooltip-Cft_Rxiw.js} +1 -1
- package/dist/elements/chunks/{triangle-alert-BlS80PUg.js → triangle-alert-B07fX3Q8.js} +1 -1
- package/dist/elements/chunks/{use-card-resolution-VSVoxDeJ.js → use-card-resolution-M36zVciB.js} +1 -1
- package/dist/elements/chunks/{video-DxTPZqT8.js → video-MKp3LmNT.js} +1 -1
- package/dist/elements/chunks/x-Ckwicf6g.js +1 -0
- package/dist/elements/coachmark.js +1 -1
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.js +1 -1
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.js +1 -1
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/embed.js +1 -1
- package/dist/elements/empty.js +1 -1
- package/dist/elements/feedback-bar.js +1 -1
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/file-upload.js +1 -1
- package/dist/elements/form.js +1 -1
- package/dist/elements/hover-card.js +1 -1
- package/dist/elements/icon.js +1 -1
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.js +1 -1
- package/dist/elements/kbd.js +1 -1
- package/dist/elements/link-preview.js +1 -1
- package/dist/elements/loader.js +1 -1
- package/dist/elements/markdown.js +1 -1
- package/dist/elements/menu.js +1 -1
- package/dist/elements/message-skills.js +1 -1
- package/dist/elements/message.js +1 -1
- package/dist/elements/model-switcher.js +1 -1
- package/dist/elements/nav.js +1 -1
- package/dist/elements/notice.js +1 -1
- package/dist/elements/pane-group.js +1 -1
- package/dist/elements/pane.js +1 -1
- package/dist/elements/popover.js +1 -1
- package/dist/elements/progress-bar.js +1 -1
- package/dist/elements/prompt-dock.js +1 -1
- package/dist/elements/prompt-input.js +1 -1
- package/dist/elements/prompt-suggestions.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +1 -1
- package/dist/elements/screen.js +1 -1
- package/dist/elements/scroll-area.js +1 -1
- package/dist/elements/scroll-button.js +1 -1
- package/dist/elements/search.js +1 -1
- package/dist/elements/segmented.js +1 -1
- package/dist/elements/separator.js +1 -1
- package/dist/elements/setting-item.js +1 -1
- package/dist/elements/settings-group.js +1 -1
- package/dist/elements/skeleton.js +1 -1
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.js +1 -1
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.js +1 -1
- package/dist/elements/tasks.js +1 -1
- package/dist/elements/text-shimmer.js +1 -1
- package/dist/elements/thinking-bar.js +1 -1
- package/dist/elements/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.js +1 -1
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/index.js +4 -5
- package/dist/kai.es.js +1 -1
- package/dist/register-impl-DcjZDELj.js +141 -0
- package/dist/ui/pane-group.d.ts +4 -2
- package/package.json +9 -3
- package/src/agent-tooling/mcp/tools/debug.ts +1 -1
- package/src/components/coachmark.tsx +7 -3
- package/src/components/component-meta.json +8 -8
- package/src/components/tasks-card.tsx +0 -1
- package/src/elements/compiled.css +2 -2
- package/src/elements/define.tsx +40 -1
- package/src/elements/search.tsx +1 -1
- package/src/ui/agent-card.tsx +28 -22
- package/src/ui/command.tsx +5 -1
- package/src/ui/pane-grid.tsx +1 -0
- package/src/ui/pane-group.tsx +52 -41
- package/src/ui/pane.tsx +1 -1
- package/dist/elements/chunks/arrow-left-C-gwlUmx.js +0 -1
- package/dist/elements/chunks/check-hGUoVIKb.js +0 -1
- package/dist/elements/chunks/chevron-down-DDoaSUtO.js +0 -1
- package/dist/elements/chunks/chevron-right-CdGEUakB.js +0 -1
- package/dist/elements/chunks/circle-BKAZdmzb.js +0 -1
- package/dist/elements/chunks/circle-check-CmYG-y3I.js +0 -1
- package/dist/elements/chunks/define-rGsaWsLL.js +0 -1
- package/dist/elements/chunks/message-square-CUg9eRCi.js +0 -1
- package/dist/elements/chunks/x-C1qtndxn.js +0 -1
- package/dist/register-impl-CYWGkgbI.js +0 -141
package/src/elements/define.tsx
CHANGED
|
@@ -137,13 +137,27 @@ export function defineWebComponent<P extends Record<string, unknown>, E = Record
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
// Props that NAME a reflected global IDL accessor (today: only `role`, from
|
|
141
|
+
// ARIAMixin's Element.prototype.role). Unlike RESERVED (which throws), these are
|
|
142
|
+
// allowed as plain styling props, but they must NEVER reach the host as an
|
|
143
|
+
// attribute: kai-message uses role='user'|'assistant' to pick its layout, and
|
|
144
|
+
// those are not valid ARIA roles, so a reflected role="user" / role="assistant"
|
|
145
|
+
// fails axe's aria-roles rule. The native IDL setter reflects to an attribute
|
|
146
|
+
// whenever the property is set BEFORE connectedCallback (e.g. in a framework
|
|
147
|
+
// `ref`: `el.role = 'user'`), which is the window before component-register
|
|
148
|
+
// installs its own (non-reflecting) per-instance accessor in initializeProps. We
|
|
149
|
+
// shadow the native accessor on the element prototype with a plain non-reflecting
|
|
150
|
+
// one, so setting the property only STORES the value (component-register still
|
|
151
|
+
// reads it back for styling) and never touches a host attribute.
|
|
152
|
+
const NON_REFLECTING = ['role'];
|
|
153
|
+
|
|
140
154
|
// Every element gets a `theme` property/attribute: 'light' | 'dark' | 'auto'
|
|
141
155
|
// (default 'auto' = follow the OS `prefers-color-scheme`). It drives a `.dark`
|
|
142
156
|
// class on an inner wrapper, which the injected kit CSS already styles — so dark
|
|
143
157
|
// mode works in standalone Shadow-DOM usage with no token duplication.
|
|
144
158
|
const defaults = { theme: 'auto', ...propDefaults };
|
|
145
159
|
|
|
146
|
-
customElement(tag, defaults, (props: typeof defaults, options: { element: object }) => {
|
|
160
|
+
const Ctor = customElement(tag, defaults, (props: typeof defaults, options: { element: object }) => {
|
|
147
161
|
const element = options.element as HTMLElement;
|
|
148
162
|
let portalNode!: HTMLDivElement;
|
|
149
163
|
|
|
@@ -215,4 +229,29 @@ export function defineWebComponent<P extends Record<string, unknown>, E = Record
|
|
|
215
229
|
</>
|
|
216
230
|
);
|
|
217
231
|
});
|
|
232
|
+
|
|
233
|
+
// Shadow any reflected native IDL accessor this element re-uses as a prop (see
|
|
234
|
+
// NON_REFLECTING) with a plain, non-reflecting store on the element prototype.
|
|
235
|
+
// This must live on the prototype (not the instance) so it is already in place
|
|
236
|
+
// when the value is set before connectedCallback, the window in which the native
|
|
237
|
+
// setter would otherwise reflect it to a host attribute. component-register reads
|
|
238
|
+
// the property back during initializeProps to seed the styling prop, and later
|
|
239
|
+
// shadows this with its own (also non-reflecting) per-instance accessor.
|
|
240
|
+
const proto = (Ctor as unknown as { prototype?: object } | undefined)?.prototype;
|
|
241
|
+
if (proto) {
|
|
242
|
+
for (const key of NON_REFLECTING) {
|
|
243
|
+
if (!(key in defaults)) continue;
|
|
244
|
+
const store = Symbol(`kai-prop:${key}`);
|
|
245
|
+
Object.defineProperty(proto, key, {
|
|
246
|
+
get(this: Record<symbol, unknown>) {
|
|
247
|
+
return this[store];
|
|
248
|
+
},
|
|
249
|
+
set(this: Record<symbol, unknown>, value: unknown) {
|
|
250
|
+
this[store] = value;
|
|
251
|
+
},
|
|
252
|
+
enumerable: true,
|
|
253
|
+
configurable: true,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
218
257
|
}
|
package/src/elements/search.tsx
CHANGED
|
@@ -157,7 +157,7 @@ defineWebComponent<Props, Events>('kai-search', {
|
|
|
157
157
|
</button>
|
|
158
158
|
</Show>
|
|
159
159
|
<Show when={!value() && props.shortcut}>
|
|
160
|
-
<Kbd keys={props.shortcut as string} size="sm"
|
|
160
|
+
<Kbd keys={props.shortcut as string} size="sm" />
|
|
161
161
|
</Show>
|
|
162
162
|
</>
|
|
163
163
|
);
|
package/src/ui/agent-card.tsx
CHANGED
|
@@ -115,27 +115,15 @@ export function AgentCard(props: AgentCardProps) {
|
|
|
115
115
|
};
|
|
116
116
|
|
|
117
117
|
return (
|
|
118
|
-
//
|
|
119
|
-
// is a
|
|
118
|
+
// A plain container (no interactive role): the agent name below is the real
|
|
119
|
+
// <button> activator, and the kebab is a separate sibling <button>, so neither
|
|
120
|
+
// interactive control is nested inside the other.
|
|
120
121
|
<div
|
|
121
|
-
role="button"
|
|
122
|
-
tabindex="0"
|
|
123
|
-
onClick={activate}
|
|
124
|
-
onKeyDown={(e) => {
|
|
125
|
-
// Only activate from the card itself, never from the nested kebab.
|
|
126
|
-
if (e.currentTarget !== e.target) return;
|
|
127
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
128
|
-
e.preventDefault();
|
|
129
|
-
activate();
|
|
130
|
-
}
|
|
131
|
-
}}
|
|
132
|
-
aria-current={local.active ? 'true' : undefined}
|
|
133
122
|
data-active={local.active ? '' : undefined}
|
|
134
123
|
data-needs-attention={local.needsAttention ? '' : undefined}
|
|
135
124
|
class={cn(
|
|
136
|
-
'group flex w-full cursor-pointer items-center gap-3 rounded-lg border px-3 py-2.5 text-left',
|
|
137
|
-
'
|
|
138
|
-
'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring',
|
|
125
|
+
'group relative flex w-full cursor-pointer items-center gap-3 rounded-lg border px-3 py-2.5 text-left',
|
|
126
|
+
'transition-[background-color,border-color,box-shadow]',
|
|
139
127
|
local.active
|
|
140
128
|
? 'border-primary bg-accent'
|
|
141
129
|
: 'border-border bg-surface hover:bg-accent',
|
|
@@ -174,9 +162,26 @@ export function AgentCard(props: AgentCardProps) {
|
|
|
174
162
|
</Show>
|
|
175
163
|
</span>
|
|
176
164
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
165
|
+
{/* The agent name is the card's primary action: a real <button> whose inset
|
|
166
|
+
::before overlay stretches the click target over the whole card, so a click
|
|
167
|
+
anywhere promotes the agent. Being a real button it is natively focusable and
|
|
168
|
+
activates on Enter / Space, so no manual key handling is needed. The overlay
|
|
169
|
+
also carries the card's focus ring (rounded to match), keeping the focused
|
|
170
|
+
card visually identical to before. */}
|
|
171
|
+
<button
|
|
172
|
+
type="button"
|
|
173
|
+
onClick={activate}
|
|
174
|
+
aria-current={local.active ? 'true' : undefined}
|
|
175
|
+
class={cn(
|
|
176
|
+
'min-w-0 flex-1 text-left outline-none',
|
|
177
|
+
"before:absolute before:inset-0 before:rounded-lg before:content-['']",
|
|
178
|
+
'focus-visible:before:ring-2 focus-visible:before:ring-inset focus-visible:before:ring-ring',
|
|
179
|
+
)}
|
|
180
|
+
>
|
|
181
|
+
<span class="block truncate text-sm font-medium leading-tight text-foreground">
|
|
182
|
+
{local.name}
|
|
183
|
+
</span>
|
|
184
|
+
</button>
|
|
180
185
|
|
|
181
186
|
<span class="flex shrink-0 items-center gap-2">
|
|
182
187
|
<Show when={local.needsAttention}>
|
|
@@ -192,12 +197,13 @@ export function AgentCard(props: AgentCardProps) {
|
|
|
192
197
|
part="menu"
|
|
193
198
|
aria-label="More"
|
|
194
199
|
onClick={(e) => {
|
|
195
|
-
//
|
|
200
|
+
// The kebab sits above the name button's stretch overlay (relative z-10),
|
|
201
|
+
// so it opens its menu without activating the card; stop propagation too.
|
|
196
202
|
e.stopPropagation();
|
|
197
203
|
local.onMenu?.(e);
|
|
198
204
|
}}
|
|
199
205
|
class={cn(
|
|
200
|
-
'inline-flex size-7 shrink-0 items-center justify-center rounded-md',
|
|
206
|
+
'relative z-10 inline-flex size-7 shrink-0 items-center justify-center rounded-md',
|
|
201
207
|
'text-muted-foreground outline-none transition-colors',
|
|
202
208
|
'hover:bg-surface-sunken hover:text-foreground',
|
|
203
209
|
'focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring',
|
package/src/ui/command.tsx
CHANGED
|
@@ -41,7 +41,11 @@ export function CommandList(props: CommandListProps): JSX.Element {
|
|
|
41
41
|
const hasItems = () => props.groups.some((g) => g.items.length > 0);
|
|
42
42
|
|
|
43
43
|
return (
|
|
44
|
-
<div
|
|
44
|
+
<div
|
|
45
|
+
role={hasItems() ? 'listbox' : undefined}
|
|
46
|
+
aria-label={hasItems() ? (props.ariaLabel ?? 'Command palette') : undefined}
|
|
47
|
+
id={props.id}
|
|
48
|
+
>
|
|
45
49
|
<Show
|
|
46
50
|
when={hasItems()}
|
|
47
51
|
fallback={
|
package/src/ui/pane-grid.tsx
CHANGED
|
@@ -108,6 +108,7 @@ export function PaneGrid(props: PaneGridProps) {
|
|
|
108
108
|
// min-w-0 / min-h-0 let the grid shrink inside a flex/grid parent so its own
|
|
109
109
|
// overflow:auto (not the parent) takes over once panes hit their minimums.
|
|
110
110
|
class={cn('grid h-full w-full min-h-0 min-w-0 overflow-auto', props.class)}
|
|
111
|
+
tabindex={0}
|
|
111
112
|
style={gridStyle()}
|
|
112
113
|
>
|
|
113
114
|
<For each={shown()}>{(pane) => pane}</For>
|
package/src/ui/pane-group.tsx
CHANGED
|
@@ -99,8 +99,10 @@ const TONE_TEXT: Record<PaneStatusTone, string> = {
|
|
|
99
99
|
* renders `children` as the active pane's body and swaps it in response to
|
|
100
100
|
* `onTabChange`. Selection-only — it never routes content itself.
|
|
101
101
|
*
|
|
102
|
-
* Accessibility: `role="
|
|
103
|
-
*
|
|
102
|
+
* Accessibility: a `role="group"` ("Open panes") strip of real `<button>` tab
|
|
103
|
+
* activators (NOT `role="tab"`, so the strip can legally own the per-tab menu/close
|
|
104
|
+
* buttons) with roving tabindex + Arrow/Home/End navigation; the active activator is
|
|
105
|
+
* marked `aria-current="true"`, and the body is the `role="tabpanel"`. Colors are all
|
|
104
106
|
* token-backed (surface / border / ring / tool-*), so it reads in light and dark.
|
|
105
107
|
* The strip, each tab, and the body are exposed via `::part(tabs|tab|body)` for
|
|
106
108
|
* the `kai-pane-group` facade. Give the group a bounded height for the body scroll.
|
|
@@ -166,57 +168,65 @@ export function PaneGroup(props: PaneGroupProps) {
|
|
|
166
168
|
// The status word shows on the active tab, on hover, and always when the tab
|
|
167
169
|
// needs attention or has errored.
|
|
168
170
|
const alwaysWord = () => isActive() || !!tab().needsAttention || tone() === 'error';
|
|
171
|
+
// Outer wrapper is a plain div (no ARIA role) so the activator button and
|
|
172
|
+
// the menu/close buttons are siblings -- no nested-interactive violation.
|
|
169
173
|
return (
|
|
170
174
|
<div
|
|
171
|
-
ref={(el) => { tabEls[tab().id] = el; }}
|
|
172
|
-
part="tab"
|
|
173
|
-
role="tab"
|
|
174
|
-
tabindex={tab().id === rovingId() ? 0 : -1}
|
|
175
|
-
aria-selected={isActive() ? 'true' : 'false'}
|
|
176
|
-
data-active={isActive() ? '' : undefined}
|
|
177
|
-
data-needs-attention={tab().needsAttention ? '' : undefined}
|
|
178
|
-
onClick={() => select(tab().id)}
|
|
179
|
-
onKeyDown={(e) => onKeyDown(e, tab())}
|
|
180
175
|
class={cn(
|
|
181
|
-
'group/tab relative flex shrink-0
|
|
182
|
-
'outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring',
|
|
176
|
+
'group/tab relative flex shrink-0 items-center rounded-md text-xs',
|
|
183
177
|
isActive()
|
|
184
178
|
? 'bg-background text-foreground shadow-sm ring-1 ring-border'
|
|
185
179
|
: 'text-muted-foreground hover:bg-hover hover:text-foreground',
|
|
186
180
|
tab().needsAttention && !isActive() && 'ring-1 ring-tool-amber/50',
|
|
187
181
|
)}
|
|
182
|
+
data-active={isActive() ? '' : undefined}
|
|
183
|
+
data-needs-attention={tab().needsAttention ? '' : undefined}
|
|
188
184
|
>
|
|
189
|
-
{/*
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
185
|
+
{/* Button-group activator (NOT role="tab") so the strip can legally own the
|
|
186
|
+
sibling menu/close buttons. aria-current marks the active pane. */}
|
|
187
|
+
<button
|
|
188
|
+
ref={(el) => { tabEls[tab().id] = el; }}
|
|
189
|
+
type="button"
|
|
190
|
+
part="tab"
|
|
191
|
+
tabindex={tab().id === rovingId() ? 0 : -1}
|
|
192
|
+
aria-current={isActive() ? 'true' : undefined}
|
|
193
|
+
onClick={() => select(tab().id)}
|
|
194
|
+
onKeyDown={(e) => onKeyDown(e, tab())}
|
|
195
|
+
class="flex cursor-pointer items-center gap-1.5 py-1 pl-1.5 outline-none transition-colors focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring"
|
|
196
|
+
>
|
|
197
|
+
{/* Numbered status badge: color = status, digit = the ⌥-jump number. */}
|
|
198
|
+
<span class="relative flex size-[18px] shrink-0" aria-hidden="true">
|
|
199
|
+
<Show when={tab().status?.pulse}>
|
|
200
|
+
<span class={cn('absolute inset-0 animate-ping rounded opacity-60 motion-reduce:hidden', TONE_BG[tone()])} />
|
|
201
|
+
</Show>
|
|
202
|
+
<span
|
|
203
|
+
class={cn(
|
|
204
|
+
'relative flex size-[18px] items-center justify-center rounded text-[11px] font-bold leading-none tabular-nums',
|
|
205
|
+
TONE_BG[tone()],
|
|
206
|
+
TONE_BADGE_FG[tone()],
|
|
207
|
+
)}
|
|
208
|
+
>
|
|
209
|
+
{num()}
|
|
210
|
+
</span>
|
|
202
211
|
</span>
|
|
203
|
-
</span>
|
|
204
212
|
|
|
205
|
-
|
|
213
|
+
<span class="max-w-[8rem] truncate font-medium">{tab().name}</span>
|
|
206
214
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
215
|
+
<Show when={tab().status?.label}>
|
|
216
|
+
<span
|
|
217
|
+
class={cn(
|
|
218
|
+
'truncate text-[10px] font-medium',
|
|
219
|
+
TONE_TEXT[tone()],
|
|
220
|
+
alwaysWord() ? 'inline' : 'hidden group-hover/tab:inline',
|
|
221
|
+
)}
|
|
222
|
+
>
|
|
223
|
+
{tab().status!.label}
|
|
224
|
+
</span>
|
|
225
|
+
</Show>
|
|
226
|
+
</button>
|
|
218
227
|
|
|
219
|
-
|
|
228
|
+
{/* menu/close are siblings to the activator, not descendants -- no nested-interactive. */}
|
|
229
|
+
<span class="ml-0.5 flex shrink-0 items-center pr-1">
|
|
220
230
|
<Show when={props.onTabMenu}>
|
|
221
231
|
<button
|
|
222
232
|
type="button"
|
|
@@ -266,7 +276,8 @@ export function PaneGroup(props: PaneGroupProps) {
|
|
|
266
276
|
>
|
|
267
277
|
<div
|
|
268
278
|
part="tabs"
|
|
269
|
-
role="
|
|
279
|
+
role="group"
|
|
280
|
+
aria-label="Open panes"
|
|
270
281
|
class="flex shrink-0 items-center gap-1 overflow-x-auto border-b border-border px-1.5 py-1"
|
|
271
282
|
>
|
|
272
283
|
<For each={tabs()}>{(tab, i) => <TabItem tab={tab} index={i()} />}</For>
|
package/src/ui/pane.tsx
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as o}from"./Icon-DLLBeLG5.js";import{c as m,m as r}from"./define-rGsaWsLL.js";var e=[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]],t=a=>m(o,r(a,{name:"ArrowLeft",iconNode:e})),p=t;export{p as a};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as m}from"./Icon-DLLBeLG5.js";import{c as o,m as r}from"./define-rGsaWsLL.js";var c=[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]],e=a=>o(m,r(a,{name:"Check",iconNode:c})),f=e;export{f as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as r}from"./Icon-DLLBeLG5.js";import{c as a,m}from"./define-rGsaWsLL.js";var e=[["path",{d:"m6 9 6 6 6-6",key:"qrunsl"}]],n=o=>a(r,m(o,{name:"ChevronDown",iconNode:e})),p=n;export{p as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as o}from"./Icon-DLLBeLG5.js";import{c as a,m as r}from"./define-rGsaWsLL.js";var t=[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]],e=m=>a(o,r(m,{name:"ChevronRight",iconNode:t})),h=e;export{h as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as c}from"./Icon-DLLBeLG5.js";import{c as m,m as o}from"./define-rGsaWsLL.js";var a=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}]],e=r=>m(c,o(r,{name:"Circle",iconNode:a})),i=e;export{i as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{I as c}from"./Icon-DLLBeLG5.js";import{c as r,m as a}from"./define-rGsaWsLL.js";var e=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]],o=m=>r(c,a(m,{name:"CircleCheck",iconNode:e})),i=o;export{i as c};
|