@adia-ai/web-components 0.0.4 → 0.0.5

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.
@@ -35,6 +35,16 @@
35
35
  "description": "Component property: resizable.",
36
36
  "type": "boolean",
37
37
  "default": false
38
+ },
39
+ "side": {
40
+ "description": "Opts a pane into horizontal-sibling chrome: suppresses the default\nfour-sided border and moves the resize grabber to the inner edge\n(right edge for `leading`, left edge for `trailing`). Also flips the\nresize-drag direction so `trailing` panes grow when dragged leftward.\nUnset keeps the pane-intrinsic chrome (full border, right-edge\ngrabber when resizable).\n",
41
+ "type": "string",
42
+ "enum": [
43
+ "",
44
+ "leading",
45
+ "trailing"
46
+ ],
47
+ "default": ""
38
48
  }
39
49
  },
40
50
  "required": [
@@ -54,6 +54,23 @@
54
54
  overflow: hidden;
55
55
  }
56
56
 
57
+ /* Side-aware border treatment.
58
+ `leading` — pane sits at the leading edge of a horizontal row, so
59
+ the only visual separator it needs is on its trailing
60
+ edge (right in LTR).
61
+ `trailing` — mirror: only a leading-edge border (left in LTR).
62
+ Default (no side attr) keeps the full 4-sided border. */
63
+ :scope[side="leading"],
64
+ :scope[side="trailing"] {
65
+ border: none;
66
+ }
67
+ :scope[side="leading"] {
68
+ border-inline-end: 1px solid var(--pane-border);
69
+ }
70
+ :scope[side="trailing"] {
71
+ border-inline-start: 1px solid var(--pane-border);
72
+ }
73
+
57
74
  /* ── Pane header ── */
58
75
  > header {
59
76
  display: flex;
@@ -101,16 +118,23 @@
101
118
  display: none;
102
119
  }
103
120
 
104
- /* Section header */
121
+ /* Section header — section's padding is the single source of horizontal inset;
122
+ content inside the section must not add its own padding. */
105
123
  > section > header {
106
124
  font-size: var(--pane-section-header-size);
107
125
  color: var(--pane-section-header-fg);
108
- padding-bottom: var(--pane-gap-xs);
126
+ padding-bottom: var(--pane-col-gap);
109
127
  text-transform: uppercase;
110
128
  letter-spacing: 0.06em;
111
129
  font-weight: var(--pane-section-header-weight);
112
130
  }
113
131
 
132
+ /* Defensive: a data-col directly under a section inherits the section's inset;
133
+ adding its own padding would compound and misalign with the section header. */
134
+ > section > [data-col] {
135
+ padding: 0;
136
+ }
137
+
114
138
  /* Section divider between sections */
115
139
  > section + section {
116
140
  border-top: 1px solid var(--pane-border);
@@ -143,11 +167,16 @@
143
167
  border-bottom: none;
144
168
  }
145
169
 
146
- /* ── Resize handle ── */
170
+ /* ── Resize handle ──
171
+ Default (no side) positions the grabber on the right edge. When
172
+ `side="trailing"` is set, flip the grabber to the left edge so it
173
+ sits on the pane's "inner" side (where it meets the sibling
174
+ content/pane in a horizontal row). `inset-inline-*` lets the rules
175
+ stay LTR/RTL-safe. */
147
176
  [slot="resize"] {
148
177
  position: absolute;
149
178
  top: 0;
150
- right: 0;
179
+ inset-inline-end: 0;
151
180
  bottom: 0;
152
181
  width: var(--pane-resize-width);
153
182
  cursor: col-resize;
@@ -156,6 +185,11 @@
156
185
  z-index: 1;
157
186
  }
158
187
 
188
+ :scope[side="trailing"] > [slot="resize"] {
189
+ inset-inline-end: auto;
190
+ inset-inline-start: 0;
191
+ }
192
+
159
193
  [slot="resize"]:hover {
160
194
  background: var(--pane-resize-fg-hover);
161
195
  }
@@ -19,6 +19,12 @@
19
19
  * resizable — boolean, enables drag-to-resize (width)
20
20
  * min-width — minimum width when resizing (default: 200)
21
21
  * max-width — maximum width when resizing (default: 600)
22
+ * side — 'leading' | 'trailing' | '' (default). When set, the
23
+ * pane is treated as a horizontal sibling: the default
24
+ * four-sided border is suppressed, only the inner-edge
25
+ * border is drawn, the resize grabber moves to that
26
+ * inner edge, and the resize-drag direction flips so
27
+ * trailing panes grow when dragged leftward.
22
28
  *
23
29
  * JS API:
24
30
  * pane.collapsed = true/false
@@ -33,6 +39,7 @@ class AdiaPane extends AdiaElement {
33
39
  resizable: { type: Boolean, default: false, reflect: true },
34
40
  minWidth: { type: Number, default: 200, attribute: 'min-width', reflect: true },
35
41
  maxWidth: { type: Number, default: 9999, attribute: 'max-width', reflect: true },
42
+ side: { type: String, default: '', reflect: true },
36
43
  };
37
44
 
38
45
  static template = () => null;
@@ -116,7 +123,12 @@ class AdiaPane extends AdiaElement {
116
123
 
117
124
  #onResizeMove = (e) => {
118
125
  if (!this.#dragging) return;
119
- const dx = e.clientX - this.#startX;
126
+ // For a trailing pane the grabber sits on the LEFT edge but the pane
127
+ // is anchored on the RIGHT side of its flex row — dragging leftward
128
+ // should GROW the pane (grabber moves further left = more width),
129
+ // not shrink it. Negate dx in that case.
130
+ const sign = this.side === 'trailing' ? -1 : 1;
131
+ const dx = (e.clientX - this.#startX) * sign;
120
132
  const w = Math.max(this.minWidth, Math.min(this.maxWidth, this.#startW + dx));
121
133
  this.style.width = `${w}px`;
122
134
  };
@@ -26,6 +26,18 @@ props:
26
26
  description: "Component property: resizable."
27
27
  type: boolean
28
28
  default: false
29
+ side:
30
+ description: |
31
+ Opts a pane into horizontal-sibling chrome: suppresses the default
32
+ four-sided border and moves the resize grabber to the inner edge
33
+ (right edge for `leading`, left edge for `trailing`). Also flips the
34
+ resize-drag direction so `trailing` panes grow when dragged leftward.
35
+ Unset keeps the pane-intrinsic chrome (full border, right-edge
36
+ grabber when resizable).
37
+ type: string
38
+ default: ""
39
+ enum: ["", leading, trailing]
40
+ reflect: true
29
41
  events: {}
30
42
  slots:
31
43
  header:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/web-components",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "AdiaUI web components — vanilla custom elements. A2UI runtime (renderer, registry, streams, wiring) lives in @adia-ai/a2ui-utils.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -16,6 +16,9 @@
16
16
  --nav-group-bg-hover: var(--a-bg-muted);
17
17
  --nav-group-icon-size: calc(var(--nav-group-row-height) - var(--a-space-2));
18
18
  --nav-group-badge-size: var(--a-ui-sm);
19
+
20
+ --nav-item-child-height: var(--a-size-sm);
21
+ --nav-item-child-font: var(--nav-group-font-size);
19
22
  }
20
23
 
21
24
  :scope {
@@ -74,8 +74,20 @@
74
74
  "Command"
75
75
  ],
76
76
  "slots": {
77
+ "description": {
78
+ "description": "Secondary metadata inside any chrome bar. Muted + --a-ui-sm size."
79
+ },
77
80
  "default": {
78
81
  "description": "Author-supplied page DOM. Expected structure — aside[data-sidebar] for navigation, main for content, optional dialog[data-command] for Cmd+K."
82
+ },
83
+ "action": {
84
+ "description": "Trailing control cluster inside any chrome bar. The first [slot=\"action\"] child pushes itself (and siblings) to the end; subsequent siblings flow with gap. Coexists with legacy <span data-spacer> / <div data-actions> hooks for one release — new code should prefer slots."
85
+ },
86
+ "heading": {
87
+ "description": "Primary label inside any chrome bar. Medium-weight + strong fg."
88
+ },
89
+ "icon": {
90
+ "description": "Leading glyph inside any chrome bar — > main > header / footer and [data-sidebar] > header / footer. Muted color, flex-align."
79
91
  }
80
92
  },
81
93
  "states": [
@@ -39,6 +39,23 @@ slots:
39
39
  description: >-
40
40
  Author-supplied page DOM. Expected structure — aside[data-sidebar] for
41
41
  navigation, main for content, optional dialog[data-command] for Cmd+K.
42
+ icon:
43
+ description: >-
44
+ Leading glyph inside any chrome bar — > main > header / footer and
45
+ [data-sidebar] > header / footer. Muted color, flex-align.
46
+ heading:
47
+ description: >-
48
+ Primary label inside any chrome bar. Medium-weight + strong fg.
49
+ description:
50
+ description: >-
51
+ Secondary metadata inside any chrome bar. Muted + --a-ui-sm size.
52
+ action:
53
+ description: >-
54
+ Trailing control cluster inside any chrome bar. The first
55
+ [slot="action"] child pushes itself (and siblings) to the end;
56
+ subsequent siblings flow with gap. Coexists with legacy
57
+ <span data-spacer> / <div data-actions> hooks for one release —
58
+ new code should prefer slots.
42
59
 
43
60
  states:
44
61
  - name: idle
@@ -13,7 +13,19 @@ app-shell-ui > main {
13
13
  }
14
14
 
15
15
  /* ── Main > header (topbar) ──
16
- Contains: sidebar toggle, breadcrumb, spacer, action buttons */
16
+ Contains: sidebar toggle, breadcrumb, spacer, action buttons.
17
+
18
+ Slot contract (shared with > main > footer and [data-sidebar] >
19
+ header/footer) — identical to card-ui / drawer-ui / adia-editor-ui:
20
+ [slot="icon"] leading glyph
21
+ [slot="heading"] primary label; strong weight + strong fg
22
+ [slot="description"] secondary metadata; muted fg + --a-ui-sm
23
+ [slot="action"] trailing cluster; first pushes to end
24
+ The legacy data-sidebar-toggle / breadcrumb-ui / <span data-spacer>
25
+ / <div data-actions> hooks (see app-shell.helpers.css) remain
26
+ fully supported — slots are additive, not a replacement. Use slots
27
+ for simpler chrome surfaces; keep breadcrumb + data-actions for
28
+ docs-style shells where those conventions carry semantic weight. */
17
29
  app-shell-ui > main > header {
18
30
  display: flex;
19
31
  align-items: center;
@@ -25,6 +37,31 @@ app-shell-ui > main > header {
25
37
  flex-shrink: 0;
26
38
  }
27
39
 
40
+ app-shell-ui > main > header > [slot="icon"] {
41
+ display: flex;
42
+ align-items: center;
43
+ flex-shrink: 0;
44
+ color: var(--page-header-fg-muted);
45
+ }
46
+ app-shell-ui > main > header > [slot="heading"] {
47
+ font-weight: var(--a-weight-medium);
48
+ color: var(--a-fg);
49
+ }
50
+ app-shell-ui > main > header > [slot="description"] {
51
+ color: var(--page-header-fg-muted);
52
+ font-size: var(--a-ui-sm);
53
+ }
54
+ app-shell-ui > main > header > [slot="action"] {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: var(--page-actions-gap);
58
+ flex-shrink: 0;
59
+ margin-inline-start: auto;
60
+ }
61
+ app-shell-ui > main > header > [slot="action"] ~ [slot="action"] {
62
+ margin-inline-start: 0;
63
+ }
64
+
28
65
  /* ── Main > section (scroll container) ──
29
66
  Wraps [data-content-root]. Scrolls vertically, hides scrollbar. */
30
67
  app-shell-ui > main > section {
@@ -39,7 +76,11 @@ app-shell-ui > main > section {
39
76
  app-shell-ui > main > section::-webkit-scrollbar { display: none; }
40
77
 
41
78
  /* ── Main > footer (status bar) ──
42
- Last child auto-pushed to trailing edge via margin-inline-start: auto. */
79
+ Legacy pattern: last-child auto-pushed to trailing edge via
80
+ margin-inline-start: auto (works when authors use a simple
81
+ <span>…</span><span>version</span> shape).
82
+ Slot pattern: same icon / heading / description / action vocabulary
83
+ as > main > header (see comment block above). */
43
84
  app-shell-ui > main > footer {
44
85
  flex-shrink: 0;
45
86
  display: flex;
@@ -53,6 +94,34 @@ app-shell-ui > main > footer {
53
94
  color: var(--page-header-fg-muted);
54
95
  }
55
96
 
56
- app-shell-ui > main > footer > :last-child {
97
+ /* Legacy: bare <span>…</span><span>version</span> shapes. Only kicks
98
+ in when NO slot="action" is present, so it doesn't fight the slot
99
+ rule below. */
100
+ app-shell-ui > main > footer:not(:has(> [slot="action"])) > :last-child {
57
101
  margin-inline-start: auto;
58
102
  }
103
+
104
+ app-shell-ui > main > footer > [slot="icon"] {
105
+ display: flex;
106
+ align-items: center;
107
+ flex-shrink: 0;
108
+ color: var(--page-header-fg-muted);
109
+ }
110
+ app-shell-ui > main > footer > [slot="heading"] {
111
+ font-weight: var(--a-weight-medium);
112
+ color: var(--a-fg);
113
+ }
114
+ app-shell-ui > main > footer > [slot="description"] {
115
+ color: var(--page-header-fg-muted);
116
+ font-size: var(--a-ui-sm);
117
+ }
118
+ app-shell-ui > main > footer > [slot="action"] {
119
+ display: flex;
120
+ align-items: center;
121
+ gap: var(--page-actions-gap);
122
+ flex-shrink: 0;
123
+ margin-inline-start: auto;
124
+ }
125
+ app-shell-ui > main > footer > [slot="action"] ~ [slot="action"] {
126
+ margin-inline-start: 0;
127
+ }
@@ -60,7 +60,13 @@
60
60
  width: var(--page-sidebar-width-trailing);
61
61
  }
62
62
 
63
- /* ── Sidebar header / footer ── */
63
+ /* ── Sidebar header / footer ──
64
+ Share the same slot contract as > main > header/footer (see
65
+ app-shell.main.css top-of-file comment): icon / heading /
66
+ description / action. Sidebar chromes are typically single-child
67
+ (a workspace-select or user-select) so the slot rules rarely come
68
+ into play — but they stay consistent for authors that compose
69
+ richer sidebar chromes. */
64
70
  [data-sidebar] > header,
65
71
  [data-sidebar] > footer {
66
72
  display: flex;
@@ -83,6 +89,36 @@
83
89
  border-top: var(--page-border);
84
90
  }
85
91
 
92
+ [data-sidebar] > header > [slot="icon"],
93
+ [data-sidebar] > footer > [slot="icon"] {
94
+ display: flex;
95
+ align-items: center;
96
+ flex-shrink: 0;
97
+ color: var(--page-header-fg-muted);
98
+ }
99
+ [data-sidebar] > header > [slot="heading"],
100
+ [data-sidebar] > footer > [slot="heading"] {
101
+ font-weight: var(--a-weight-medium);
102
+ color: var(--a-fg);
103
+ }
104
+ [data-sidebar] > header > [slot="description"],
105
+ [data-sidebar] > footer > [slot="description"] {
106
+ color: var(--page-header-fg-muted);
107
+ font-size: var(--a-ui-sm);
108
+ }
109
+ [data-sidebar] > header > [slot="action"],
110
+ [data-sidebar] > footer > [slot="action"] {
111
+ display: flex;
112
+ align-items: center;
113
+ gap: var(--page-actions-gap);
114
+ flex-shrink: 0;
115
+ margin-inline-start: auto;
116
+ }
117
+ [data-sidebar] > header > [slot="action"] ~ [slot="action"],
118
+ [data-sidebar] > footer > [slot="action"] ~ [slot="action"] {
119
+ margin-inline-start: 0;
120
+ }
121
+
86
122
  /* ── Sidebar section (scrollable body) ── */
87
123
  [data-sidebar] > section {
88
124
  flex: 1;
@@ -108,8 +108,6 @@
108
108
  --nav-item-trailing-border: var(--a-border-subtle);
109
109
  --nav-item-trailing-radius: var(--a-radius-sm);
110
110
  --nav-item-trailing-px: var(--a-space-0-5);
111
- --nav-item-child-height: var(--a-size-sm);
112
- --nav-item-child-font: var(--a-ui-sm);
113
111
 
114
112
  /* Global icon size bump */
115
113
  --a-icon-size: 18px;