@adea-ai/ui 0.63.4 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +74 -0
  2. package/dist/NOTICE +81 -0
  3. package/dist/components/conversation/busy-send-button.d.ts +28 -0
  4. package/dist/components/conversation/busy-send-button.d.ts.map +1 -0
  5. package/dist/components/conversation/busy-send-button.js +184 -0
  6. package/dist/components/conversation/busy-send-button.js.map +1 -0
  7. package/dist/components/conversation/index.d.ts +1 -0
  8. package/dist/components/conversation/index.d.ts.map +1 -1
  9. package/dist/components/conversation/index.js +2 -1
  10. package/dist/components/layout/split-layout/drop.d.ts +13 -0
  11. package/dist/components/layout/split-layout/drop.d.ts.map +1 -0
  12. package/dist/components/layout/split-layout/drop.js +50 -0
  13. package/dist/components/layout/split-layout/drop.js.map +1 -0
  14. package/dist/components/layout/split-layout/geometry.d.ts +14 -0
  15. package/dist/components/layout/split-layout/geometry.d.ts.map +1 -0
  16. package/dist/components/layout/split-layout/geometry.js +44 -0
  17. package/dist/components/layout/split-layout/geometry.js.map +1 -0
  18. package/dist/components/layout/split-layout/index.d.ts +5 -0
  19. package/dist/components/layout/split-layout/index.d.ts.map +1 -0
  20. package/dist/components/layout/split-layout/index.js +4 -0
  21. package/dist/components/layout/split-layout/model.d.ts +51 -0
  22. package/dist/components/layout/split-layout/model.d.ts.map +1 -0
  23. package/dist/components/layout/split-layout/model.js +255 -0
  24. package/dist/components/layout/split-layout/model.js.map +1 -0
  25. package/dist/components/layout/split-layout/split-layout.d.ts +30 -0
  26. package/dist/components/layout/split-layout/split-layout.d.ts.map +1 -0
  27. package/dist/components/layout/split-layout/split-layout.js +312 -0
  28. package/dist/components/layout/split-layout/split-layout.js.map +1 -0
  29. package/dist/index.d.ts +1 -0
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +5 -1
  32. package/dist/lib/tokens.d.ts +3 -0
  33. package/dist/lib/tokens.d.ts.map +1 -1
  34. package/dist/lib/tokens.js +3 -0
  35. package/dist/lib/tokens.js.map +1 -1
  36. package/dist/r/conversation.json +7 -0
  37. package/dist/r/registry.json +48 -0
  38. package/dist/r/split-layout.json +41 -0
  39. package/dist/r/src/components/conversation/busy-send-button.tsx +161 -0
  40. package/dist/r/src/components/conversation/index.ts +1 -0
  41. package/dist/r/src/components/layout/split-layout/drop.ts +29 -0
  42. package/dist/r/src/components/layout/split-layout/geometry.ts +40 -0
  43. package/dist/r/src/components/layout/split-layout/index.ts +4 -0
  44. package/dist/r/src/components/layout/split-layout/model.ts +336 -0
  45. package/dist/r/src/components/layout/split-layout/split-layout.tsx +346 -0
  46. package/dist/r/src/lib/tokens.ts +3 -0
  47. package/dist/r/src/styles/theme.css +9 -0
  48. package/package.json +10 -2
  49. package/registry.json +48 -0
  50. package/src/components/conversation/busy-send-button.tsx +161 -0
  51. package/src/components/conversation/index.ts +1 -0
  52. package/src/components/layout/split-layout/drop.ts +29 -0
  53. package/src/components/layout/split-layout/geometry.ts +40 -0
  54. package/src/components/layout/split-layout/index.ts +4 -0
  55. package/src/components/layout/split-layout/model.ts +336 -0
  56. package/src/components/layout/split-layout/split-layout.tsx +346 -0
  57. package/src/index.ts +1 -0
  58. package/src/lib/tokens.ts +3 -0
  59. package/src/styles/theme.css +9 -0
@@ -826,6 +826,15 @@
826
826
  --height-control-md: var(--control-height-md);
827
827
  --height-control-lg: var(--control-height-lg);
828
828
  --height-control-xl: var(--control-height-xl);
829
+ /* Tailwind `size-*` prefers --size-* and then --spacing-*. Without this
830
+ bridge, square icon controls use the padding ladder below for both axes.
831
+ Height, square size and inline padding remain distinct semantic contracts. */
832
+ --size-control-2xs: var(--control-height-2xs);
833
+ --size-control-xs: var(--control-height-xs);
834
+ --size-control-sm: var(--control-height-sm);
835
+ --size-control-md: var(--control-height-md);
836
+ --size-control-lg: var(--control-height-lg);
837
+ --size-control-xl: var(--control-height-xl);
829
838
  --height-row-sm: var(--row-height-sm);
830
839
  --height-row-md: var(--row-height-md);
831
840
  --height-row-lg: var(--row-height-lg);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adea-ai/ui",
3
- "version": "0.63.4",
3
+ "version": "0.65.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",
@@ -64,7 +64,13 @@
64
64
  "./base.css": "./src/styles/base.css",
65
65
  "./fonts.css": "./src/styles/fonts.css",
66
66
  "./registry.json": "./registry.json",
67
- "./package.json": "./package.json"
67
+ "./package.json": "./package.json",
68
+ "./components/layout/split-layout/model": {
69
+ "types": "./dist/components/layout/split-layout/model.d.ts",
70
+ "solid": "./src/components/layout/split-layout/model.ts",
71
+ "development": "./src/components/layout/split-layout/model.ts",
72
+ "import": "./dist/components/layout/split-layout/model.js"
73
+ }
68
74
  },
69
75
  "scripts": {
70
76
  "build": "vite build && tsc -p tsconfig.json --emitDeclarationOnly",
@@ -78,7 +84,9 @@
78
84
  "check:tree-shaking": "bun scripts/check-tree-shaking.ts",
79
85
  "theme:build": "bun scripts/build-default-theme.ts",
80
86
  "theme:check": "bun scripts/build-default-theme.ts --check",
87
+ "check:packed-layout": "node scripts/check-packed-layout.mjs",
81
88
  "check:packed-consumer": "bun scripts/check-packed-consumer.ts",
89
+ "check:packed-layout-renderer": "bun scripts/check-packed-layout-renderer.ts",
82
90
  "check:packed-appearance": "bun scripts/check-packed-appearance.ts"
83
91
  },
84
92
  "dependencies": {
package/registry.json CHANGED
@@ -1529,6 +1529,47 @@
1529
1529
  }
1530
1530
  ]
1531
1531
  },
1532
+ {
1533
+ "name": "split-layout",
1534
+ "type": "registry:ui",
1535
+ "title": "Split Layout",
1536
+ "description": "Issue-backed binary layout with stable opaque leaf owners; scoped persistence belongs to the host.",
1537
+ "dependencies": [
1538
+ "@corvu/resizable@^0.2.5",
1539
+ "lucide-solid@^1.48.0"
1540
+ ],
1541
+ "registryDependencies": [
1542
+ "@adea-ai/ui/button",
1543
+ "lib"
1544
+ ],
1545
+ "files": [
1546
+ {
1547
+ "path": "src/components/layout/split-layout/drop.ts",
1548
+ "type": "registry:ui",
1549
+ "target": "components/layout/split-layout/drop.ts"
1550
+ },
1551
+ {
1552
+ "path": "src/components/layout/split-layout/geometry.ts",
1553
+ "type": "registry:ui",
1554
+ "target": "components/layout/split-layout/geometry.ts"
1555
+ },
1556
+ {
1557
+ "path": "src/components/layout/split-layout/index.ts",
1558
+ "type": "registry:ui",
1559
+ "target": "components/layout/split-layout/index.ts"
1560
+ },
1561
+ {
1562
+ "path": "src/components/layout/split-layout/model.ts",
1563
+ "type": "registry:ui",
1564
+ "target": "components/layout/split-layout/model.ts"
1565
+ },
1566
+ {
1567
+ "path": "src/components/layout/split-layout/split-layout.tsx",
1568
+ "type": "registry:ui",
1569
+ "target": "components/layout/split-layout/split-layout.tsx"
1570
+ }
1571
+ ]
1572
+ },
1532
1573
  {
1533
1574
  "name": "status-bar",
1534
1575
  "type": "registry:ui",
@@ -1781,13 +1822,20 @@
1781
1822
  ],
1782
1823
  "registryDependencies": [
1783
1824
  "@adea-ai/ui/button",
1825
+ "@adea-ai/ui/button-group",
1784
1826
  "@adea-ai/ui/code-block",
1827
+ "@adea-ai/ui/dropdown-menu",
1785
1828
  "@adea-ai/ui/kbd",
1786
1829
  "@adea-ai/ui/spinner",
1787
1830
  "@adea-ai/ui/textarea",
1788
1831
  "lib"
1789
1832
  ],
1790
1833
  "files": [
1834
+ {
1835
+ "path": "src/components/conversation/busy-send-button.tsx",
1836
+ "type": "registry:ui",
1837
+ "target": "components/conversation/busy-send-button.tsx"
1838
+ },
1791
1839
  {
1792
1840
  "path": "src/components/conversation/conversation-avatar.tsx",
1793
1841
  "type": "registry:ui",
@@ -0,0 +1,161 @@
1
+ /*
2
+ * Substantially translated from KiroCrew website/src/components/BusySendButton.tsx
3
+ * at 283e136c0f902e965a535a7c9548c57c7504fed0.
4
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
5
+ * Licensed under Apache-2.0; see LICENSE and NOTICE.
6
+ */
7
+ import { ArrowUpFromLine, ChevronDown, Target } from 'lucide-solid'
8
+ import { For, Show, createUniqueId } from 'solid-js'
9
+ import { Button } from '../ui/button/button'
10
+ import { ButtonGroup } from '../ui/button-group/button-group'
11
+ import {
12
+ DropdownMenu,
13
+ DropdownMenuContent,
14
+ DropdownMenuRadioGroup,
15
+ DropdownMenuRadioItem,
16
+ DropdownMenuSeparator,
17
+ DropdownMenuTrigger,
18
+ } from '../ui/dropdown-menu/dropdown-menu'
19
+
20
+ export type BusySendMode = 'steer' | 'queue'
21
+ export type BusySendButtonProps = {
22
+ mode: BusySendMode
23
+ onModeChange: (mode: BusySendMode) => void
24
+ /** Executes only the selected action. The host owns the draft and command. */
25
+ onFire: () => void
26
+ /** An empty draft or pending delivery disables fire, but not choosing a mode. */
27
+ disabled?: boolean
28
+ /** Disable mode selection when the host has no authority to change preferences. */
29
+ selectionDisabled?: boolean
30
+ /** Host capability/authority failures: unavailable rows remain visible. */
31
+ unavailable?: Partial<Record<BusySendMode, string>>
32
+ /** Supply only when the host implements this alternate-action keyboard chord. */
33
+ alternateActionHint?: string
34
+ }
35
+
36
+ const modes: BusySendMode[] = ['steer', 'queue']
37
+ const labels = { steer: 'Steer', queue: 'Queue' }
38
+ const descriptions = {
39
+ steer: 'Act on this right away',
40
+ queue: 'Run after the current work finishes',
41
+ }
42
+
43
+ /**
44
+ * Kiro's split action/menu composition, with app-owned mode and availability.
45
+ * Picking a mode never fires it. Persistence, migration and same-session
46
+ * broadcasts belong to the consumer; this component has no storage or global listeners.
47
+ * Kobalte replaces the donor manual portal/positioning and document listeners:
48
+ * arrows wrap and selection/Escape return focus. Kobalte 0.13.14 prevents Tab
49
+ * without moving focus, so a menu-scoped translation of the donor Tab cycle
50
+ * fills that gap. It discovers enabled radio rows only; no document trap.
51
+ * No force-reset timer or runtime authority is translated from the adjacent
52
+ * donor stop flow.
53
+ */
54
+ export function BusySendButton(props: BusySendButtonProps) {
55
+ const reasonId = createUniqueId()
56
+ const reason = () => props.unavailable?.[props.mode]
57
+ const variant = () => (props.mode === 'steer' ? ('default' as const) : ('secondary' as const))
58
+ return (
59
+ <div data-slot="busy-send-button" class="flex w-fit flex-col gap-1">
60
+ <DropdownMenu placement="top-end" modal={false}>
61
+ <ButtonGroup label="Send while busy">
62
+ <Button
63
+ type="button"
64
+ size="icon-sm"
65
+ variant={variant()}
66
+ aria-label={props.mode === 'steer' ? 'Steer' : 'Queue message'}
67
+ aria-describedby={reason() ? reasonId : undefined}
68
+ disabled={props.disabled || !!reason()}
69
+ onClick={() => {
70
+ if (!props.disabled && !reason()) props.onFire()
71
+ }}
72
+ >
73
+ <Show when={props.mode === 'steer'} fallback={<ArrowUpFromLine />}>
74
+ <Target />
75
+ </Show>
76
+ </Button>
77
+ <DropdownMenuTrigger
78
+ as={Button}
79
+ type="button"
80
+ size="icon-sm"
81
+ variant={variant()}
82
+ aria-label="Send options"
83
+ disabled={props.selectionDisabled}
84
+ >
85
+ <ChevronDown />
86
+ </DropdownMenuTrigger>
87
+ </ButtonGroup>
88
+ <DropdownMenuContent hideArrow>
89
+ <DropdownMenuRadioGroup
90
+ on:keydown={(event: KeyboardEvent) => {
91
+ if (
92
+ event.key !== 'Tab' ||
93
+ event.isComposing ||
94
+ !(event.currentTarget instanceof HTMLElement)
95
+ )
96
+ return
97
+ const rows = Array.from(
98
+ event.currentTarget.querySelectorAll<HTMLElement>(
99
+ '[role="menuitemradio"]:not([aria-disabled="true"])'
100
+ )
101
+ )
102
+ if (!rows.length) return
103
+ event.preventDefault()
104
+ event.stopPropagation()
105
+ const active = document.activeElement
106
+ const index = active instanceof HTMLElement ? rows.indexOf(active) : -1
107
+ const next =
108
+ index < 0
109
+ ? event.shiftKey
110
+ ? rows.length - 1
111
+ : 0
112
+ : (index + (event.shiftKey ? -1 : 1) + rows.length) % rows.length
113
+ rows[next]?.focus()
114
+ }}
115
+ value={props.mode}
116
+ onChange={(value) => {
117
+ if (
118
+ (value === 'steer' || value === 'queue') &&
119
+ !props.selectionDisabled &&
120
+ !props.unavailable?.[value]
121
+ )
122
+ props.onModeChange(value)
123
+ }}
124
+ >
125
+ <For each={modes}>
126
+ {(mode) => (
127
+ <DropdownMenuRadioItem
128
+ value={mode}
129
+ closeOnSelect
130
+ disabled={props.selectionDisabled || !!props.unavailable?.[mode]}
131
+ >
132
+ <Show when={mode === 'steer'} fallback={<ArrowUpFromLine />}>
133
+ <Target />
134
+ </Show>
135
+ <div class="flex min-w-0 flex-col gap-1">
136
+ <span>{labels[mode]}</span>
137
+ <span class="text-muted-foreground text-xs">{descriptions[mode]}</span>
138
+ <Show when={props.unavailable?.[mode]}>
139
+ {(message) => <span class="text-muted-foreground text-xs">{message()}</span>}
140
+ </Show>
141
+ </div>
142
+ </DropdownMenuRadioItem>
143
+ )}
144
+ </For>
145
+ </DropdownMenuRadioGroup>
146
+ <Show when={props.alternateActionHint}>
147
+ <DropdownMenuSeparator />
148
+ <p class="text-muted-foreground px-2 py-1 text-xs">{props.alternateActionHint}</p>
149
+ </Show>
150
+ </DropdownMenuContent>
151
+ </DropdownMenu>
152
+ <Show when={reason()}>
153
+ {(message) => (
154
+ <p id={reasonId} class="text-muted-foreground text-xs">
155
+ {message()}
156
+ </p>
157
+ )}
158
+ </Show>
159
+ </div>
160
+ )
161
+ }
@@ -1,3 +1,4 @@
1
+ export { BusySendButton, type BusySendMode, type BusySendButtonProps } from './busy-send-button'
1
2
  export {
2
3
  ConversationAvatar,
3
4
  type ConversationAvatarKind,
@@ -0,0 +1,29 @@
1
+ import type { SplitLayoutBranch } from './model'
2
+ export type PaneDropIntent = {
3
+ direction: SplitLayoutBranch['direction']
4
+ placement: 'before' | 'after'
5
+ }
6
+ /** Accepted Adea closest-edge intent; invalid/outside geometry cannot authorize a move. */
7
+ export function paneDropIntent(
8
+ clientX: number,
9
+ clientY: number,
10
+ rect: { left: number; top: number; width: number; height: number }
11
+ ): PaneDropIntent | undefined {
12
+ const { left, top, width, height } = rect
13
+ if (
14
+ ![clientX, clientY, left, top, width, height].every(Number.isFinite) ||
15
+ width <= 0 ||
16
+ height <= 0
17
+ )
18
+ return
19
+ const x = clientX - left
20
+ const y = clientY - top
21
+ if (x < 0 || y < 0 || x > width || y > height) return
22
+ const candidates: { distance: number; intent: PaneDropIntent }[] = [
23
+ { distance: x, intent: { direction: 'row', placement: 'before' } },
24
+ { distance: width - x, intent: { direction: 'row', placement: 'after' } },
25
+ { distance: y, intent: { direction: 'column', placement: 'before' } },
26
+ { distance: height - y, intent: { direction: 'column', placement: 'after' } },
27
+ ]
28
+ return candidates.reduce((best, next) => (next.distance < best.distance ? next : best)).intent
29
+ }
@@ -0,0 +1,40 @@
1
+ /*
2
+ * Translated from Muxy/Models/Workspace/SplitNode.swift areaFrames at
3
+ * 5c5be8697c57a2fe70cda97fdbaf7c912e2e31b6.
4
+ * Copyright (c) 2026 Muxy. SPDX-License-Identifier: MIT. See NOTICE.
5
+ */
6
+ import {
7
+ MAX_SPLIT_RATIO,
8
+ MIN_SPLIT_RATIO,
9
+ type SplitLayoutLeaf,
10
+ type SplitLayoutNode,
11
+ } from './model'
12
+ export type LayoutRect = Readonly<{ x: number; y: number; width: number; height: number }>
13
+ export type LayoutFrame<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{
14
+ node: SplitLayoutNode<L>
15
+ rect: LayoutRect
16
+ }>
17
+ /** Stable leaf DOM can use these fractions without being reparented into a changing nested tree. */
18
+ export function computeLayoutFrames<L extends SplitLayoutLeaf>(
19
+ node: SplitLayoutNode<L>
20
+ ): ReadonlyMap<string, LayoutFrame<L>> {
21
+ const frames = new Map<string, LayoutFrame<L>>()
22
+ const visit = (current: SplitLayoutNode<L>, rect: LayoutRect) => {
23
+ frames.set(current.id, { node: current, rect })
24
+ if (current.kind === 'leaf') return
25
+ const ratio = Number.isFinite(current.ratio)
26
+ ? Math.min(MAX_SPLIT_RATIO, Math.max(MIN_SPLIT_RATIO, current.ratio))
27
+ : 0.5
28
+ const row = current.direction === 'row'
29
+ const first = row
30
+ ? { ...rect, width: rect.width * ratio }
31
+ : { ...rect, height: rect.height * ratio }
32
+ const second = row
33
+ ? { ...rect, x: rect.x + first.width, width: rect.width - first.width }
34
+ : { ...rect, y: rect.y + first.height, height: rect.height - first.height }
35
+ visit(current.children[0], first)
36
+ visit(current.children[1], second)
37
+ }
38
+ visit(node, { x: 0, y: 0, width: 1, height: 1 })
39
+ return frames
40
+ }
@@ -0,0 +1,4 @@
1
+ /** Issue-backed binary layout with stable opaque leaf owners; scoped persistence belongs to the host. */
2
+ export * from './model'
3
+ export * from './geometry'
4
+ export * from './split-layout'