@haiyue/ui 0.1.0 → 0.1.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.
Files changed (69) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/LICENSE +1 -1
  3. package/README.md +226 -2
  4. package/dist/border-beam.d.ts +26 -0
  5. package/dist/border-beam.js +174 -0
  6. package/dist/button.d.ts +10 -0
  7. package/dist/button.js +85 -0
  8. package/dist/checkbox.d.ts +2 -2
  9. package/dist/checkbox.js +11 -11
  10. package/dist/context-menu.d.ts +6 -6
  11. package/dist/context-menu.js +11 -9
  12. package/dist/dialog.d.ts +5 -5
  13. package/dist/dialog.js +18 -18
  14. package/dist/drawer.d.ts +49 -0
  15. package/dist/drawer.js +377 -0
  16. package/dist/dropdown.d.ts +9 -9
  17. package/dist/dropdown.js +9 -9
  18. package/dist/expandable.d.ts +32 -0
  19. package/dist/expandable.js +136 -0
  20. package/dist/history-controls.d.ts +40 -0
  21. package/dist/history-controls.js +123 -0
  22. package/dist/index.d.ts +36 -31
  23. package/dist/index.js +35 -85
  24. package/dist/input.d.ts +5 -5
  25. package/dist/input.js +13 -13
  26. package/dist/menu-shared.d.ts +4 -4
  27. package/dist/menu-shared.js +4 -4
  28. package/dist/notification.d.ts +55 -0
  29. package/dist/notification.js +357 -0
  30. package/dist/radio.d.ts +2 -2
  31. package/dist/radio.js +12 -12
  32. package/dist/range.d.ts +74 -0
  33. package/dist/range.js +393 -0
  34. package/dist/select.d.ts +6 -6
  35. package/dist/select.js +20 -20
  36. package/dist/split.d.ts +6 -6
  37. package/dist/split.js +14 -14
  38. package/dist/tabs.d.ts +10 -6
  39. package/dist/tabs.js +94 -30
  40. package/dist/tooltip.d.ts +4 -4
  41. package/dist/tooltip.js +9 -9
  42. package/dist/tree-model.d.ts +15 -7
  43. package/dist/tree-model.js +93 -80
  44. package/dist/tree-node.d.ts +3 -3
  45. package/dist/tree-node.js +3 -3
  46. package/dist/tree-types.d.ts +14 -14
  47. package/dist/tree.d.ts +9 -7
  48. package/dist/tree.js +68 -66
  49. package/dist/virtual-list.d.ts +61 -0
  50. package/dist/virtual-list.js +324 -0
  51. package/package.json +100 -7
  52. package/themes/haiyue-dark.css +46 -0
  53. package/themes/haiyue-light.css +46 -0
  54. package/src/checkbox.ts +0 -215
  55. package/src/context-menu.ts +0 -248
  56. package/src/dialog.ts +0 -195
  57. package/src/dropdown.ts +0 -249
  58. package/src/index.ts +0 -192
  59. package/src/input.ts +0 -150
  60. package/src/menu-shared.ts +0 -65
  61. package/src/radio.ts +0 -216
  62. package/src/select.ts +0 -271
  63. package/src/split.ts +0 -306
  64. package/src/tabs.ts +0 -182
  65. package/src/tooltip.ts +0 -321
  66. package/src/tree-model.ts +0 -305
  67. package/src/tree-node.ts +0 -67
  68. package/src/tree-types.ts +0 -47
  69. package/src/tree.ts +0 -1002
@@ -0,0 +1,324 @@
1
+ const DEFAULT_ITEM_HEIGHT = 40;
2
+ const DEFAULT_HEIGHT = 320;
3
+ const DEFAULT_OVERSCAN = 3;
4
+ function finiteNumber(value, fallback) {
5
+ return Number.isFinite(value) ? value : fallback;
6
+ }
7
+ export function calculateVirtualListRange(itemCount, itemHeight, viewportHeight, scrollTop, overscan) {
8
+ const count = Math.max(0, Math.floor(finiteNumber(itemCount, 0)));
9
+ const rowHeight = Math.max(1, finiteNumber(itemHeight, DEFAULT_ITEM_HEIGHT));
10
+ const visibleHeight = Math.max(0, finiteNumber(viewportHeight, 0));
11
+ const requestedOffset = Math.max(0, finiteNumber(scrollTop, 0));
12
+ const maximumOffset = Math.max(0, count * rowHeight - visibleHeight);
13
+ // Layout and ResizeObserver notifications can briefly observe a stale
14
+ // scrollTop after the viewport or item count changes. Keep the calculated
15
+ // window inside the collection instead of emitting an empty render range.
16
+ const offset = Math.min(requestedOffset, maximumOffset);
17
+ const buffer = Math.max(0, Math.floor(finiteNumber(overscan, DEFAULT_OVERSCAN)));
18
+ const visibleStartIndex = Math.min(count, Math.floor(offset / rowHeight));
19
+ const visibleEndIndex = Math.min(count, Math.ceil((offset + visibleHeight) / rowHeight));
20
+ return {
21
+ startIndex: Math.max(0, visibleStartIndex - buffer),
22
+ endIndex: Math.min(count, visibleEndIndex + buffer),
23
+ visibleStartIndex,
24
+ visibleEndIndex,
25
+ };
26
+ }
27
+ export class HYVirtualList extends HTMLElement {
28
+ _viewport = document.createElement('div');
29
+ _spacer = document.createElement('div');
30
+ _window = document.createElement('div');
31
+ _empty = document.createElement('div');
32
+ _items = [];
33
+ _renderItem = null;
34
+ _itemHeight = DEFAULT_ITEM_HEIGHT;
35
+ _height = DEFAULT_HEIGHT;
36
+ _overscan = DEFAULT_OVERSCAN;
37
+ _range = null;
38
+ _resizeObserver = null;
39
+ _connected = false;
40
+ _onScroll = () => this._renderWindow();
41
+ _onClick = (event) => this._handleClick(event);
42
+ static get observedAttributes() {
43
+ return ['item-height', 'height', 'overscan', 'aria-label'];
44
+ }
45
+ constructor() {
46
+ super();
47
+ const root = this.attachShadow({ mode: 'open' });
48
+ const style = document.createElement('style');
49
+ style.textContent = `
50
+ :host {
51
+ display: block;
52
+ min-width: 0;
53
+ color: var(--hy-text-color, #eef3ff);
54
+ font: 13px/1.4 system-ui, sans-serif;
55
+ }
56
+ .viewport {
57
+ position: relative;
58
+ height: var(--hy-virtual-list-height, 320px);
59
+ overflow-x: hidden;
60
+ overflow-y: auto;
61
+ overscroll-behavior: contain;
62
+ border: 1px solid var(--hy-border-color, #3d4654);
63
+ border-radius: var(--hy-virtual-list-border-radius, 6px);
64
+ outline: none;
65
+ background: var(--hy-surface-color, #171d27);
66
+ scrollbar-color: var(--hy-border-strong-color, #596779) var(--hy-input-bg-color, #111722);
67
+ scrollbar-width: thin;
68
+ }
69
+ .viewport:focus-visible {
70
+ border-color: var(--hy-focus-border-color, #79a8ff);
71
+ box-shadow: 0 0 0 2px var(--hy-focus-ring-color, rgba(121, 168, 255, 0.22));
72
+ }
73
+ .viewport::-webkit-scrollbar { width: var(--hy-virtual-list-scrollbar-size, 10px); }
74
+ .viewport::-webkit-scrollbar-track { background: var(--hy-input-bg-color, #111722); }
75
+ .viewport::-webkit-scrollbar-thumb {
76
+ min-height: 28px;
77
+ border: 2px solid var(--hy-input-bg-color, #111722);
78
+ border-radius: 999px;
79
+ background: var(--hy-border-strong-color, #596779);
80
+ }
81
+ .viewport::-webkit-scrollbar-thumb:hover { background: var(--hy-accent-color, #79a8ff); }
82
+ .spacer { position: relative; width: 100%; }
83
+ .window { position: absolute; inset: 0 0 auto; will-change: transform; }
84
+ .hy-virtual-list-item {
85
+ display: flex;
86
+ align-items: center;
87
+ width: 100%;
88
+ overflow: hidden;
89
+ border-bottom: 1px solid var(--hy-border-subtle-color, var(--hy-border-color, #3d4654));
90
+ background: var(--hy-surface-color, #171d27);
91
+ box-sizing: border-box;
92
+ }
93
+ .hy-virtual-list-item:hover { background: var(--hy-hover-bg-color, #273241); }
94
+ .empty {
95
+ display: grid;
96
+ height: 100%;
97
+ place-items: center;
98
+ padding: 24px;
99
+ color: var(--hy-secondary-text-color, #9aa8ba);
100
+ box-sizing: border-box;
101
+ }
102
+ .empty[hidden], .spacer[hidden] { display: none; }
103
+ `;
104
+ this._viewport.className = 'viewport';
105
+ this._viewport.tabIndex = 0;
106
+ this._viewport.setAttribute('role', 'list');
107
+ this._spacer.className = 'spacer';
108
+ this._window.className = 'window';
109
+ this._spacer.append(this._window);
110
+ this._empty.className = 'empty';
111
+ this._empty.hidden = true;
112
+ const emptySlot = document.createElement('slot');
113
+ emptySlot.name = 'empty';
114
+ emptySlot.textContent = 'No data';
115
+ this._empty.append(emptySlot);
116
+ this._viewport.append(this._spacer, this._empty);
117
+ root.append(style, this._viewport);
118
+ }
119
+ connectedCallback() {
120
+ if (this._connected)
121
+ return;
122
+ this._connected = true;
123
+ this._syncAttributes();
124
+ this._viewport.addEventListener('scroll', this._onScroll, { passive: true });
125
+ this._window.addEventListener('click', this._onClick);
126
+ if (typeof ResizeObserver !== 'undefined') {
127
+ this._resizeObserver = new ResizeObserver(() => this._renderWindow());
128
+ this._resizeObserver.observe(this._viewport);
129
+ }
130
+ this._renderWindow(true);
131
+ }
132
+ disconnectedCallback() {
133
+ if (!this._connected)
134
+ return;
135
+ this._connected = false;
136
+ this._viewport.removeEventListener('scroll', this._onScroll);
137
+ this._window.removeEventListener('click', this._onClick);
138
+ this._resizeObserver?.disconnect();
139
+ this._resizeObserver = null;
140
+ this._replaceRows([]);
141
+ }
142
+ attributeChangedCallback() {
143
+ this._syncAttributes();
144
+ }
145
+ get items() { return this._items; }
146
+ set items(value) {
147
+ this._items = Array.isArray(value) ? value : [];
148
+ this._clampScrollTop();
149
+ this.refresh();
150
+ }
151
+ get renderItem() { return this._renderItem; }
152
+ set renderItem(value) {
153
+ this._renderItem = typeof value === 'function' ? value : null;
154
+ this.refresh();
155
+ }
156
+ get itemHeight() { return this._itemHeight; }
157
+ set itemHeight(value) {
158
+ const next = Math.max(1, finiteNumber(Number(value), DEFAULT_ITEM_HEIGHT));
159
+ if (next === this._itemHeight && this.getAttribute('item-height') === String(next))
160
+ return;
161
+ this._itemHeight = next;
162
+ this._reflectNumber('item-height', next);
163
+ this.refresh();
164
+ }
165
+ get height() { return this._height; }
166
+ set height(value) {
167
+ const next = Math.max(1, finiteNumber(Number(value), DEFAULT_HEIGHT));
168
+ if (next === this._height && this.getAttribute('height') === String(next))
169
+ return;
170
+ this._height = next;
171
+ this._reflectNumber('height', next);
172
+ this._applyHeight();
173
+ this.refresh();
174
+ }
175
+ get overscan() { return this._overscan; }
176
+ set overscan(value) {
177
+ const next = Math.max(0, Math.floor(finiteNumber(Number(value), DEFAULT_OVERSCAN)));
178
+ if (next === this._overscan && this.getAttribute('overscan') === String(next))
179
+ return;
180
+ this._overscan = next;
181
+ this._reflectNumber('overscan', next);
182
+ this.refresh();
183
+ }
184
+ refresh() {
185
+ this._range = null;
186
+ this._renderWindow(true);
187
+ }
188
+ scrollToIndex(index, align = 'auto') {
189
+ if (this._items.length === 0)
190
+ return;
191
+ const targetIndex = Math.min(this._items.length - 1, Math.max(0, Math.floor(finiteNumber(index, 0))));
192
+ const viewportHeight = this._viewport.clientHeight || this._height;
193
+ const itemStart = targetIndex * this._itemHeight;
194
+ const itemEnd = itemStart + this._itemHeight;
195
+ const currentStart = this._viewport.scrollTop;
196
+ const currentEnd = currentStart + viewportHeight;
197
+ let nextScrollTop = currentStart;
198
+ if (align === 'start')
199
+ nextScrollTop = itemStart;
200
+ else if (align === 'center')
201
+ nextScrollTop = itemStart - (viewportHeight - this._itemHeight) / 2;
202
+ else if (align === 'end')
203
+ nextScrollTop = itemEnd - viewportHeight;
204
+ else if (itemStart < currentStart)
205
+ nextScrollTop = itemStart;
206
+ else if (itemEnd > currentEnd)
207
+ nextScrollTop = itemEnd - viewportHeight;
208
+ const maxScrollTop = Math.max(0, this._items.length * this._itemHeight - viewportHeight);
209
+ this._viewport.scrollTop = Math.min(maxScrollTop, Math.max(0, nextScrollTop));
210
+ this._renderWindow();
211
+ }
212
+ _syncAttributes() {
213
+ this._itemHeight = this._readPositiveAttribute('item-height', DEFAULT_ITEM_HEIGHT);
214
+ this._height = this._readPositiveAttribute('height', DEFAULT_HEIGHT);
215
+ this._overscan = this._readNonNegativeAttribute('overscan', DEFAULT_OVERSCAN);
216
+ const label = this.getAttribute('aria-label');
217
+ if (label)
218
+ this._viewport.setAttribute('aria-label', label);
219
+ else
220
+ this._viewport.removeAttribute('aria-label');
221
+ this._applyHeight();
222
+ this.refresh();
223
+ }
224
+ _applyHeight() {
225
+ if (this.hasAttribute('height'))
226
+ this._viewport.style.height = `${this._height}px`;
227
+ else
228
+ this._viewport.style.removeProperty('height');
229
+ }
230
+ _readPositiveAttribute(name, fallback) {
231
+ if (!this.hasAttribute(name))
232
+ return fallback;
233
+ const value = Number(this.getAttribute(name));
234
+ return Number.isFinite(value) && value > 0 ? value : fallback;
235
+ }
236
+ _readNonNegativeAttribute(name, fallback) {
237
+ if (!this.hasAttribute(name))
238
+ return fallback;
239
+ const value = Number(this.getAttribute(name));
240
+ return Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
241
+ }
242
+ _reflectNumber(name, value) {
243
+ const text = String(value);
244
+ if (this.getAttribute(name) !== text)
245
+ this.setAttribute(name, text);
246
+ }
247
+ _clampScrollTop() {
248
+ const viewportHeight = this._viewport.clientHeight || this._height;
249
+ const maxScrollTop = Math.max(0, this._items.length * this._itemHeight - viewportHeight);
250
+ if (this._viewport.scrollTop > maxScrollTop)
251
+ this._viewport.scrollTop = maxScrollTop;
252
+ }
253
+ _renderWindow(force = false) {
254
+ const count = this._items.length;
255
+ this._spacer.hidden = count === 0;
256
+ this._empty.hidden = count !== 0;
257
+ this._spacer.style.height = `${count * this._itemHeight}px`;
258
+ if (count === 0) {
259
+ this._replaceRows([]);
260
+ this._range = null;
261
+ return;
262
+ }
263
+ const range = calculateVirtualListRange(count, this._itemHeight, this._viewport.clientHeight || this._height, this._viewport.scrollTop, this._overscan);
264
+ if (!force && this._range
265
+ && range.startIndex === this._range.startIndex
266
+ && range.endIndex === this._range.endIndex)
267
+ return;
268
+ this._range = range;
269
+ this._window.style.transform = `translateY(${range.startIndex * this._itemHeight}px)`;
270
+ const rows = [];
271
+ for (let index = range.startIndex; index < range.endIndex; index += 1) {
272
+ const item = this._items[index];
273
+ const row = document.createElement('div');
274
+ row.className = 'hy-virtual-list-item';
275
+ row.dataset.hyVirtualListGenerated = '';
276
+ row.dataset.index = String(index);
277
+ row.style.height = `${this._itemHeight}px`;
278
+ row.setAttribute('role', 'listitem');
279
+ row.setAttribute('aria-posinset', String(index + 1));
280
+ row.setAttribute('aria-setsize', String(count));
281
+ const content = this._renderItem
282
+ ? this._renderItem(item, index)
283
+ : String(item ?? '');
284
+ if (content instanceof Node)
285
+ row.append(content);
286
+ else
287
+ row.textContent = String(content);
288
+ rows.push(row);
289
+ }
290
+ this._replaceRows(rows);
291
+ this.dispatchEvent(new CustomEvent('visible-range-change', {
292
+ bubbles: true,
293
+ composed: true,
294
+ detail: {
295
+ ...range,
296
+ renderedCount: range.endIndex - range.startIndex,
297
+ total: count,
298
+ },
299
+ }));
300
+ }
301
+ _handleClick(event) {
302
+ const target = event.target instanceof Element ? event.target.closest('.hy-virtual-list-item') : null;
303
+ const index = Number(target?.dataset.index);
304
+ if (!target || !Number.isInteger(index) || index < 0 || index >= this._items.length)
305
+ return;
306
+ const item = this._items[index];
307
+ this.dispatchEvent(new CustomEvent('item-click', {
308
+ bubbles: true,
309
+ composed: true,
310
+ detail: { item, index },
311
+ }));
312
+ }
313
+ _replaceRows(rows) {
314
+ // Keep the recycled window entirely inside the shadow tree. Slotted light
315
+ // DOM rows can be redistributed asynchronously while a host application is
316
+ // also updating, which may briefly paint rows from the previous range.
317
+ this._window.replaceChildren(...rows);
318
+ }
319
+ }
320
+ export function defineVirtualListComponents() {
321
+ if (!customElements.get('hy-virtual-list'))
322
+ customElements.define('hy-virtual-list', HYVirtualList);
323
+ }
324
+ //# sourceMappingURL=virtual-list.js.map
package/package.json CHANGED
@@ -1,24 +1,104 @@
1
1
  {
2
2
  "name": "@haiyue/ui",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "private": false,
5
5
  "description": "Dependency-free Web Components for HaiyueStudio tools and products.",
6
6
  "license": "MIT",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "git+https://github.com/HaiyueStudio/UI.git",
10
- "directory": "ui"
9
+ "url": "git+https://github.com/HaiyueStudio/UI.git"
11
10
  },
11
+ "homepage": "https://haiyuestudio.github.io/UI/",
12
12
  "type": "module",
13
13
  "main": "dist/index.js",
14
14
  "module": "dist/index.js",
15
15
  "types": "dist/index.d.ts",
16
- "sideEffects": false,
16
+ "sideEffects": [
17
+ "./themes/*.css"
18
+ ],
17
19
  "exports": {
18
20
  ".": {
19
- "source": "./src/index.ts",
20
21
  "types": "./dist/index.d.ts",
21
22
  "import": "./dist/index.js"
23
+ },
24
+ "./border-beam": {
25
+ "types": "./dist/border-beam.d.ts",
26
+ "import": "./dist/border-beam.js"
27
+ },
28
+ "./button": {
29
+ "types": "./dist/button.d.ts",
30
+ "import": "./dist/button.js"
31
+ },
32
+ "./checkbox": {
33
+ "types": "./dist/checkbox.d.ts",
34
+ "import": "./dist/checkbox.js"
35
+ },
36
+ "./context-menu": {
37
+ "types": "./dist/context-menu.d.ts",
38
+ "import": "./dist/context-menu.js"
39
+ },
40
+ "./dialog": {
41
+ "types": "./dist/dialog.d.ts",
42
+ "import": "./dist/dialog.js"
43
+ },
44
+ "./drawer": {
45
+ "types": "./dist/drawer.d.ts",
46
+ "import": "./dist/drawer.js"
47
+ },
48
+ "./dropdown": {
49
+ "types": "./dist/dropdown.d.ts",
50
+ "import": "./dist/dropdown.js"
51
+ },
52
+ "./input": {
53
+ "types": "./dist/input.d.ts",
54
+ "import": "./dist/input.js"
55
+ },
56
+ "./notification": {
57
+ "types": "./dist/notification.d.ts",
58
+ "import": "./dist/notification.js"
59
+ },
60
+ "./history-controls": {
61
+ "types": "./dist/history-controls.d.ts",
62
+ "import": "./dist/history-controls.js"
63
+ },
64
+ "./virtual-list": {
65
+ "types": "./dist/virtual-list.d.ts",
66
+ "import": "./dist/virtual-list.js"
67
+ },
68
+ "./radio": {
69
+ "types": "./dist/radio.d.ts",
70
+ "import": "./dist/radio.js"
71
+ },
72
+ "./range": {
73
+ "types": "./dist/range.d.ts",
74
+ "import": "./dist/range.js"
75
+ },
76
+ "./select": {
77
+ "types": "./dist/select.d.ts",
78
+ "import": "./dist/select.js"
79
+ },
80
+ "./split": {
81
+ "types": "./dist/split.d.ts",
82
+ "import": "./dist/split.js"
83
+ },
84
+ "./tabs": {
85
+ "types": "./dist/tabs.d.ts",
86
+ "import": "./dist/tabs.js"
87
+ },
88
+ "./tooltip": {
89
+ "types": "./dist/tooltip.d.ts",
90
+ "import": "./dist/tooltip.js"
91
+ },
92
+ "./tree": {
93
+ "types": "./dist/tree.d.ts",
94
+ "import": "./dist/tree.js"
95
+ },
96
+ "./themes/light.css": "./themes/haiyue-light.css",
97
+ "./themes/dark.css": "./themes/haiyue-dark.css",
98
+ "./package.json": "./package.json",
99
+ "./expandable": {
100
+ "types": "./dist/expandable.d.ts",
101
+ "import": "./dist/expandable.js"
22
102
  }
23
103
  },
24
104
  "engines": {
@@ -30,17 +110,30 @@
30
110
  },
31
111
  "files": [
32
112
  "README.md",
113
+ "CHANGELOG.md",
33
114
  "LICENSE",
34
115
  "dist/**/*.js",
35
116
  "dist/**/*.d.ts",
36
- "src/**/*"
117
+ "themes/*.css"
37
118
  ],
38
119
  "scripts": {
39
120
  "build": "tsc -p tsconfig.json",
121
+ "build:pages": "npm run build && node scripts/build-pages.mjs",
122
+ "check:pages": "npm run build:pages && node scripts/verify-pages.mjs",
123
+ "examples": "npm run build && node scripts/serve-examples.mjs",
40
124
  "typecheck": "tsc -p tsconfig.json --noEmit",
41
- "test": "npm run build && node --test \"test/**/*.test.mjs\""
125
+ "test": "npm run build && node --test \"test/**/*.test.mjs\"",
126
+ "prepack": "npm run typecheck && npm test && npm run api:check",
127
+ "pack:candidate": "node scripts/pack-candidate.mjs",
128
+ "api:check": "node scripts/api-surface.mjs --check",
129
+ "api:update": "node scripts/api-surface.mjs --write",
130
+ "test:browser": "npm run build && playwright test",
131
+ "verify:package": "npm run prepack && node scripts/verify-package.mjs",
132
+ "release:check": "npm run verify:package && npm run test:browser && npm run check:pages"
42
133
  },
43
134
  "devDependencies": {
135
+ "@playwright/test": "^1.63.0",
136
+ "rollup": "^4.63.4",
44
137
  "typescript": "^5.2.0"
45
138
  }
46
139
  }
@@ -0,0 +1,46 @@
1
+ /*
2
+ * Haiyue Nightfall — deep navy surfaces, blue-violet energy and gold detail.
3
+ * Apply with data-hy-theme="dark" on :root or on a component subtree.
4
+ */
5
+ :root[data-hy-theme="dark"],
6
+ [data-hy-theme="dark"] {
7
+ color-scheme: dark;
8
+ --hy-theme-name: "Haiyue Nightfall";
9
+ --hy-canvas-color: #080b21;
10
+ --hy-surface-color: #10132f;
11
+ --hy-surface-elevated-color: #181a40;
12
+ --hy-panel-bg-color: #121633;
13
+ --hy-input-bg-color: #0c1029;
14
+ --hy-menu-bg-color: #171a3e;
15
+ --hy-text-color: #eef0ff;
16
+ --hy-secondary-text-color: #a9add4;
17
+ --hy-muted-color: #747ba8;
18
+ --hy-disabled-text-color: #656b96;
19
+ --hy-border-color: #343765;
20
+ --hy-border-strong-color: #484b84;
21
+ --hy-hover-border-color: #6468aa;
22
+ --hy-hover-bg-color: #242752;
23
+ --hy-menu-item-hover-bg-color: #302d6b;
24
+ --hy-accent-color: #8177f2;
25
+ --hy-accent-strong-color: #554cc6;
26
+ --hy-selected-bg-color: #554cc6;
27
+ --hy-selected-text-color: #ffffff;
28
+ --hy-active-text-color: #f5f2ff;
29
+ --hy-focus-border-color: #9c8fff;
30
+ --hy-focus-ring-color: rgba(137, 121, 255, 0.28);
31
+ --hy-invalid-border-color: #dd6f91;
32
+ --hy-menu-shadow-color: rgba(1, 2, 15, 0.52);
33
+ --hy-dialog-shadow-color: rgba(1, 2, 18, 0.62);
34
+ --hy-dialog-backdrop-color: rgba(3, 4, 20, 0.72);
35
+ --hy-dialog-bg-color: #17193b;
36
+ --hy-dialog-body-color: #b8badb;
37
+ --hy-dialog-footer-bg-color: #11142f;
38
+ --hy-tooltip-bg-color: #090b20;
39
+ --hy-tooltip-text-color: #f4f2ff;
40
+ --hy-tooltip-border-color: rgba(150, 137, 255, 0.28);
41
+ --hy-tooltip-shadow-color: rgba(0, 0, 10, 0.5);
42
+ --hy-drop-indicator-color: #9287ff;
43
+ --hy-drop-target-bg-color: #29265b;
44
+ --hy-theme-glow-color: #5149b9;
45
+ --hy-theme-highlight-color: #e1b952;
46
+ }
@@ -0,0 +1,46 @@
1
+ /*
2
+ * Haiyue Moonlight — moon-white surfaces, pale ice blue and cyan highlights.
3
+ * Apply with data-hy-theme="light" on :root or on a component subtree.
4
+ */
5
+ :root[data-hy-theme="light"],
6
+ [data-hy-theme="light"] {
7
+ color-scheme: light;
8
+ --hy-theme-name: "Haiyue Moonlight";
9
+ --hy-canvas-color: #edf8ff;
10
+ --hy-surface-color: #f7fbff;
11
+ --hy-surface-elevated-color: #ffffff;
12
+ --hy-panel-bg-color: #f2f8fd;
13
+ --hy-input-bg-color: #ffffff;
14
+ --hy-menu-bg-color: #fbfdff;
15
+ --hy-text-color: #183247;
16
+ --hy-secondary-text-color: #5f7c92;
17
+ --hy-muted-color: #8aa3b6;
18
+ --hy-disabled-text-color: #9baebb;
19
+ --hy-border-color: #c7ddea;
20
+ --hy-border-strong-color: #a8cde0;
21
+ --hy-hover-border-color: #86bad3;
22
+ --hy-hover-bg-color: #e5f3fb;
23
+ --hy-menu-item-hover-bg-color: #dceff9;
24
+ --hy-accent-color: #55b4da;
25
+ --hy-accent-strong-color: #2c88b0;
26
+ --hy-selected-bg-color: #2f8cb5;
27
+ --hy-selected-text-color: #ffffff;
28
+ --hy-active-text-color: #153b54;
29
+ --hy-focus-border-color: #3aa6d1;
30
+ --hy-focus-ring-color: rgba(58, 166, 209, 0.24);
31
+ --hy-invalid-border-color: #d15f79;
32
+ --hy-menu-shadow-color: rgba(50, 91, 118, 0.18);
33
+ --hy-dialog-shadow-color: rgba(42, 82, 109, 0.22);
34
+ --hy-dialog-backdrop-color: rgba(55, 92, 116, 0.24);
35
+ --hy-dialog-bg-color: #ffffff;
36
+ --hy-dialog-body-color: #527187;
37
+ --hy-dialog-footer-bg-color: #f0f7fc;
38
+ --hy-tooltip-bg-color: #214d68;
39
+ --hy-tooltip-text-color: #f7fcff;
40
+ --hy-tooltip-border-color: rgba(255, 255, 255, 0.28);
41
+ --hy-tooltip-shadow-color: rgba(35, 75, 101, 0.2);
42
+ --hy-drop-indicator-color: #38a9d7;
43
+ --hy-drop-target-bg-color: #d9effa;
44
+ --hy-theme-glow-color: #b9e9fb;
45
+ --hy-theme-highlight-color: #a99bea;
46
+ }