@haiyue/ui 0.1.0 → 0.1.1

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 (66) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +166 -2
  3. package/dist/border-beam.d.ts +26 -0
  4. package/dist/border-beam.js +172 -0
  5. package/dist/button.d.ts +10 -0
  6. package/dist/button.js +85 -0
  7. package/dist/checkbox.d.ts +2 -2
  8. package/dist/checkbox.js +11 -11
  9. package/dist/context-menu.d.ts +6 -6
  10. package/dist/context-menu.js +11 -9
  11. package/dist/dialog.d.ts +5 -5
  12. package/dist/dialog.js +18 -18
  13. package/dist/drawer.d.ts +49 -0
  14. package/dist/drawer.js +377 -0
  15. package/dist/dropdown.d.ts +9 -9
  16. package/dist/dropdown.js +9 -9
  17. package/dist/history-controls.d.ts +40 -0
  18. package/dist/history-controls.js +123 -0
  19. package/dist/index.d.ts +34 -31
  20. package/dist/index.js +32 -85
  21. package/dist/input.d.ts +5 -5
  22. package/dist/input.js +13 -13
  23. package/dist/menu-shared.d.ts +4 -4
  24. package/dist/menu-shared.js +4 -4
  25. package/dist/notification.d.ts +55 -0
  26. package/dist/notification.js +357 -0
  27. package/dist/radio.d.ts +2 -2
  28. package/dist/radio.js +12 -12
  29. package/dist/range.d.ts +74 -0
  30. package/dist/range.js +393 -0
  31. package/dist/select.d.ts +6 -6
  32. package/dist/select.js +20 -20
  33. package/dist/split.d.ts +6 -6
  34. package/dist/split.js +14 -14
  35. package/dist/tabs.d.ts +6 -6
  36. package/dist/tabs.js +14 -14
  37. package/dist/tooltip.d.ts +4 -4
  38. package/dist/tooltip.js +9 -9
  39. package/dist/tree-model.d.ts +15 -7
  40. package/dist/tree-model.js +93 -80
  41. package/dist/tree-node.d.ts +3 -3
  42. package/dist/tree-node.js +3 -3
  43. package/dist/tree-types.d.ts +14 -14
  44. package/dist/tree.d.ts +9 -7
  45. package/dist/tree.js +68 -66
  46. package/dist/virtual-list.d.ts +61 -0
  47. package/dist/virtual-list.js +322 -0
  48. package/package.json +87 -11
  49. package/themes/haiyue-dark.css +46 -0
  50. package/themes/haiyue-light.css +46 -0
  51. package/src/checkbox.ts +0 -215
  52. package/src/context-menu.ts +0 -248
  53. package/src/dialog.ts +0 -195
  54. package/src/dropdown.ts +0 -249
  55. package/src/index.ts +0 -192
  56. package/src/input.ts +0 -150
  57. package/src/menu-shared.ts +0 -65
  58. package/src/radio.ts +0 -216
  59. package/src/select.ts +0 -271
  60. package/src/split.ts +0 -306
  61. package/src/tabs.ts +0 -182
  62. package/src/tooltip.ts +0 -321
  63. package/src/tree-model.ts +0 -305
  64. package/src/tree-node.ts +0 -67
  65. package/src/tree-types.ts +0 -47
  66. package/src/tree.ts +0 -1002
@@ -0,0 +1,322 @@
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 offset = Math.max(0, finiteNumber(scrollTop, 0));
12
+ const buffer = Math.max(0, Math.floor(finiteNumber(overscan, DEFAULT_OVERSCAN)));
13
+ const visibleStartIndex = Math.min(count, Math.floor(offset / rowHeight));
14
+ const visibleEndIndex = Math.min(count, Math.ceil((offset + visibleHeight) / rowHeight));
15
+ return {
16
+ startIndex: Math.max(0, visibleStartIndex - buffer),
17
+ endIndex: Math.min(count, visibleEndIndex + buffer),
18
+ visibleStartIndex,
19
+ visibleEndIndex,
20
+ };
21
+ }
22
+ export class HYVirtualList extends HTMLElement {
23
+ _viewport = document.createElement('div');
24
+ _spacer = document.createElement('div');
25
+ _window = document.createElement('div');
26
+ _empty = document.createElement('div');
27
+ _items = [];
28
+ _renderItem = null;
29
+ _itemHeight = DEFAULT_ITEM_HEIGHT;
30
+ _height = DEFAULT_HEIGHT;
31
+ _overscan = DEFAULT_OVERSCAN;
32
+ _range = null;
33
+ _resizeObserver = null;
34
+ _connected = false;
35
+ _onScroll = () => this._renderWindow();
36
+ _onClick = (event) => this._handleClick(event);
37
+ static get observedAttributes() {
38
+ return ['item-height', 'height', 'overscan', 'aria-label'];
39
+ }
40
+ constructor() {
41
+ super();
42
+ const root = this.attachShadow({ mode: 'open' });
43
+ const style = document.createElement('style');
44
+ style.textContent = `
45
+ :host {
46
+ display: block;
47
+ min-width: 0;
48
+ color: var(--hy-text-color, #eef3ff);
49
+ font: 13px/1.4 system-ui, sans-serif;
50
+ }
51
+ .viewport {
52
+ position: relative;
53
+ height: var(--hy-virtual-list-height, 320px);
54
+ overflow-x: hidden;
55
+ overflow-y: auto;
56
+ overscroll-behavior: contain;
57
+ border: 1px solid var(--hy-border-color, #3d4654);
58
+ border-radius: var(--hy-virtual-list-border-radius, 6px);
59
+ outline: none;
60
+ background: var(--hy-surface-color, #171d27);
61
+ scrollbar-color: var(--hy-border-strong-color, #596779) var(--hy-input-bg-color, #111722);
62
+ scrollbar-width: thin;
63
+ }
64
+ .viewport:focus-visible {
65
+ border-color: var(--hy-focus-border-color, #79a8ff);
66
+ box-shadow: 0 0 0 2px var(--hy-focus-ring-color, rgba(121, 168, 255, 0.22));
67
+ }
68
+ .viewport::-webkit-scrollbar { width: var(--hy-virtual-list-scrollbar-size, 10px); }
69
+ .viewport::-webkit-scrollbar-track { background: var(--hy-input-bg-color, #111722); }
70
+ .viewport::-webkit-scrollbar-thumb {
71
+ min-height: 28px;
72
+ border: 2px solid var(--hy-input-bg-color, #111722);
73
+ border-radius: 999px;
74
+ background: var(--hy-border-strong-color, #596779);
75
+ }
76
+ .viewport::-webkit-scrollbar-thumb:hover { background: var(--hy-accent-color, #79a8ff); }
77
+ .spacer { position: relative; width: 100%; }
78
+ .window { position: absolute; inset: 0 0 auto; will-change: transform; }
79
+ ::slotted(.hy-virtual-list-item) {
80
+ display: flex;
81
+ align-items: center;
82
+ width: 100%;
83
+ overflow: hidden;
84
+ border-bottom: 1px solid var(--hy-border-subtle-color, var(--hy-border-color, #3d4654));
85
+ background: var(--hy-surface-color, #171d27);
86
+ box-sizing: border-box;
87
+ }
88
+ ::slotted(.hy-virtual-list-item:hover) { background: var(--hy-hover-bg-color, #273241); }
89
+ .empty {
90
+ display: grid;
91
+ height: 100%;
92
+ place-items: center;
93
+ padding: 24px;
94
+ color: var(--hy-secondary-text-color, #9aa8ba);
95
+ box-sizing: border-box;
96
+ }
97
+ .empty[hidden], .spacer[hidden] { display: none; }
98
+ `;
99
+ this._viewport.className = 'viewport';
100
+ this._viewport.tabIndex = 0;
101
+ this._viewport.setAttribute('role', 'list');
102
+ this._spacer.className = 'spacer';
103
+ this._window.className = 'window';
104
+ const itemSlot = document.createElement('slot');
105
+ itemSlot.name = 'items';
106
+ this._window.append(itemSlot);
107
+ this._spacer.append(this._window);
108
+ this._empty.className = 'empty';
109
+ this._empty.hidden = true;
110
+ const emptySlot = document.createElement('slot');
111
+ emptySlot.name = 'empty';
112
+ emptySlot.textContent = 'No data';
113
+ this._empty.append(emptySlot);
114
+ this._viewport.append(this._spacer, this._empty);
115
+ root.append(style, this._viewport);
116
+ }
117
+ connectedCallback() {
118
+ if (this._connected)
119
+ return;
120
+ this._connected = true;
121
+ this._syncAttributes();
122
+ this._viewport.addEventListener('scroll', this._onScroll, { passive: true });
123
+ this._window.addEventListener('click', this._onClick);
124
+ if (typeof ResizeObserver !== 'undefined') {
125
+ this._resizeObserver = new ResizeObserver(() => this._renderWindow());
126
+ this._resizeObserver.observe(this._viewport);
127
+ }
128
+ this._renderWindow(true);
129
+ }
130
+ disconnectedCallback() {
131
+ if (!this._connected)
132
+ return;
133
+ this._connected = false;
134
+ this._viewport.removeEventListener('scroll', this._onScroll);
135
+ this._window.removeEventListener('click', this._onClick);
136
+ this._resizeObserver?.disconnect();
137
+ this._resizeObserver = null;
138
+ this._replaceRows([]);
139
+ }
140
+ attributeChangedCallback() {
141
+ this._syncAttributes();
142
+ }
143
+ get items() { return this._items; }
144
+ set items(value) {
145
+ this._items = Array.isArray(value) ? value : [];
146
+ this._clampScrollTop();
147
+ this.refresh();
148
+ }
149
+ get renderItem() { return this._renderItem; }
150
+ set renderItem(value) {
151
+ this._renderItem = typeof value === 'function' ? value : null;
152
+ this.refresh();
153
+ }
154
+ get itemHeight() { return this._itemHeight; }
155
+ set itemHeight(value) {
156
+ const next = Math.max(1, finiteNumber(Number(value), DEFAULT_ITEM_HEIGHT));
157
+ if (next === this._itemHeight && this.getAttribute('item-height') === String(next))
158
+ return;
159
+ this._itemHeight = next;
160
+ this._reflectNumber('item-height', next);
161
+ this.refresh();
162
+ }
163
+ get height() { return this._height; }
164
+ set height(value) {
165
+ const next = Math.max(1, finiteNumber(Number(value), DEFAULT_HEIGHT));
166
+ if (next === this._height && this.getAttribute('height') === String(next))
167
+ return;
168
+ this._height = next;
169
+ this._reflectNumber('height', next);
170
+ this._applyHeight();
171
+ this.refresh();
172
+ }
173
+ get overscan() { return this._overscan; }
174
+ set overscan(value) {
175
+ const next = Math.max(0, Math.floor(finiteNumber(Number(value), DEFAULT_OVERSCAN)));
176
+ if (next === this._overscan && this.getAttribute('overscan') === String(next))
177
+ return;
178
+ this._overscan = next;
179
+ this._reflectNumber('overscan', next);
180
+ this.refresh();
181
+ }
182
+ refresh() {
183
+ this._range = null;
184
+ this._renderWindow(true);
185
+ }
186
+ scrollToIndex(index, align = 'auto') {
187
+ if (this._items.length === 0)
188
+ return;
189
+ const targetIndex = Math.min(this._items.length - 1, Math.max(0, Math.floor(finiteNumber(index, 0))));
190
+ const viewportHeight = this._viewport.clientHeight || this._height;
191
+ const itemStart = targetIndex * this._itemHeight;
192
+ const itemEnd = itemStart + this._itemHeight;
193
+ const currentStart = this._viewport.scrollTop;
194
+ const currentEnd = currentStart + viewportHeight;
195
+ let nextScrollTop = currentStart;
196
+ if (align === 'start')
197
+ nextScrollTop = itemStart;
198
+ else if (align === 'center')
199
+ nextScrollTop = itemStart - (viewportHeight - this._itemHeight) / 2;
200
+ else if (align === 'end')
201
+ nextScrollTop = itemEnd - viewportHeight;
202
+ else if (itemStart < currentStart)
203
+ nextScrollTop = itemStart;
204
+ else if (itemEnd > currentEnd)
205
+ nextScrollTop = itemEnd - viewportHeight;
206
+ const maxScrollTop = Math.max(0, this._items.length * this._itemHeight - viewportHeight);
207
+ this._viewport.scrollTop = Math.min(maxScrollTop, Math.max(0, nextScrollTop));
208
+ this._renderWindow();
209
+ }
210
+ _syncAttributes() {
211
+ this._itemHeight = this._readPositiveAttribute('item-height', DEFAULT_ITEM_HEIGHT);
212
+ this._height = this._readPositiveAttribute('height', DEFAULT_HEIGHT);
213
+ this._overscan = this._readNonNegativeAttribute('overscan', DEFAULT_OVERSCAN);
214
+ const label = this.getAttribute('aria-label');
215
+ if (label)
216
+ this._viewport.setAttribute('aria-label', label);
217
+ else
218
+ this._viewport.removeAttribute('aria-label');
219
+ this._applyHeight();
220
+ this.refresh();
221
+ }
222
+ _applyHeight() {
223
+ if (this.hasAttribute('height'))
224
+ this._viewport.style.height = `${this._height}px`;
225
+ else
226
+ this._viewport.style.removeProperty('height');
227
+ }
228
+ _readPositiveAttribute(name, fallback) {
229
+ if (!this.hasAttribute(name))
230
+ return fallback;
231
+ const value = Number(this.getAttribute(name));
232
+ return Number.isFinite(value) && value > 0 ? value : fallback;
233
+ }
234
+ _readNonNegativeAttribute(name, fallback) {
235
+ if (!this.hasAttribute(name))
236
+ return fallback;
237
+ const value = Number(this.getAttribute(name));
238
+ return Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
239
+ }
240
+ _reflectNumber(name, value) {
241
+ const text = String(value);
242
+ if (this.getAttribute(name) !== text)
243
+ this.setAttribute(name, text);
244
+ }
245
+ _clampScrollTop() {
246
+ const viewportHeight = this._viewport.clientHeight || this._height;
247
+ const maxScrollTop = Math.max(0, this._items.length * this._itemHeight - viewportHeight);
248
+ if (this._viewport.scrollTop > maxScrollTop)
249
+ this._viewport.scrollTop = maxScrollTop;
250
+ }
251
+ _renderWindow(force = false) {
252
+ const count = this._items.length;
253
+ this._spacer.hidden = count === 0;
254
+ this._empty.hidden = count !== 0;
255
+ this._spacer.style.height = `${count * this._itemHeight}px`;
256
+ if (count === 0) {
257
+ this._replaceRows([]);
258
+ this._range = null;
259
+ return;
260
+ }
261
+ const range = calculateVirtualListRange(count, this._itemHeight, this._viewport.clientHeight || this._height, this._viewport.scrollTop, this._overscan);
262
+ if (!force && this._range
263
+ && range.startIndex === this._range.startIndex
264
+ && range.endIndex === this._range.endIndex)
265
+ return;
266
+ this._range = range;
267
+ this._window.style.transform = `translateY(${range.startIndex * this._itemHeight}px)`;
268
+ const rows = [];
269
+ for (let index = range.startIndex; index < range.endIndex; index += 1) {
270
+ const item = this._items[index];
271
+ const row = document.createElement('div');
272
+ row.className = 'hy-virtual-list-item';
273
+ row.slot = 'items';
274
+ row.dataset.hyVirtualListGenerated = '';
275
+ row.dataset.index = String(index);
276
+ row.style.height = `${this._itemHeight}px`;
277
+ row.setAttribute('role', 'listitem');
278
+ row.setAttribute('aria-posinset', String(index + 1));
279
+ row.setAttribute('aria-setsize', String(count));
280
+ const content = this._renderItem
281
+ ? this._renderItem(item, index)
282
+ : String(item ?? '');
283
+ if (content instanceof Node)
284
+ row.append(content);
285
+ else
286
+ row.textContent = String(content);
287
+ rows.push(row);
288
+ }
289
+ this._replaceRows(rows);
290
+ this.dispatchEvent(new CustomEvent('visible-range-change', {
291
+ bubbles: true,
292
+ composed: true,
293
+ detail: {
294
+ ...range,
295
+ renderedCount: range.endIndex - range.startIndex,
296
+ total: count,
297
+ },
298
+ }));
299
+ }
300
+ _handleClick(event) {
301
+ const target = event.target instanceof Element ? event.target.closest('.hy-virtual-list-item') : null;
302
+ const index = Number(target?.dataset.index);
303
+ if (!target || !Number.isInteger(index) || index < 0 || index >= this._items.length)
304
+ return;
305
+ const item = this._items[index];
306
+ this.dispatchEvent(new CustomEvent('item-click', {
307
+ bubbles: true,
308
+ composed: true,
309
+ detail: { item, index },
310
+ }));
311
+ }
312
+ _replaceRows(rows) {
313
+ for (const row of this.querySelectorAll(':scope > [data-hy-virtual-list-generated]'))
314
+ row.remove();
315
+ this.append(...rows);
316
+ }
317
+ }
318
+ export function defineVirtualListComponents() {
319
+ if (!customElements.get('hy-virtual-list'))
320
+ customElements.define('hy-virtual-list', HYVirtualList);
321
+ }
322
+ //# sourceMappingURL=virtual-list.js.map
package/package.json CHANGED
@@ -1,29 +1,102 @@
1
1
  {
2
2
  "name": "@haiyue/ui",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
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
  },
12
11
  "type": "module",
13
12
  "main": "dist/index.js",
14
13
  "module": "dist/index.js",
15
14
  "types": "dist/index.d.ts",
16
- "sideEffects": false,
15
+ "sideEffects": [
16
+ "./themes/*.css"
17
+ ],
17
18
  "exports": {
18
19
  ".": {
19
- "source": "./src/index.ts",
20
20
  "types": "./dist/index.d.ts",
21
21
  "import": "./dist/index.js"
22
- }
23
- },
24
- "engines": {
25
- "node": ">=22"
22
+ },
23
+ "./border-beam": {
24
+ "types": "./dist/border-beam.d.ts",
25
+ "import": "./dist/border-beam.js"
26
+ },
27
+ "./button": {
28
+ "types": "./dist/button.d.ts",
29
+ "import": "./dist/button.js"
30
+ },
31
+ "./checkbox": {
32
+ "types": "./dist/checkbox.d.ts",
33
+ "import": "./dist/checkbox.js"
34
+ },
35
+ "./context-menu": {
36
+ "types": "./dist/context-menu.d.ts",
37
+ "import": "./dist/context-menu.js"
38
+ },
39
+ "./dialog": {
40
+ "types": "./dist/dialog.d.ts",
41
+ "import": "./dist/dialog.js"
42
+ },
43
+ "./drawer": {
44
+ "types": "./dist/drawer.d.ts",
45
+ "import": "./dist/drawer.js"
46
+ },
47
+ "./dropdown": {
48
+ "types": "./dist/dropdown.d.ts",
49
+ "import": "./dist/dropdown.js"
50
+ },
51
+ "./input": {
52
+ "types": "./dist/input.d.ts",
53
+ "import": "./dist/input.js"
54
+ },
55
+ "./notification": {
56
+ "types": "./dist/notification.d.ts",
57
+ "import": "./dist/notification.js"
58
+ },
59
+ "./history-controls": {
60
+ "types": "./dist/history-controls.d.ts",
61
+ "import": "./dist/history-controls.js"
62
+ },
63
+ "./virtual-list": {
64
+ "types": "./dist/virtual-list.d.ts",
65
+ "import": "./dist/virtual-list.js"
66
+ },
67
+ "./radio": {
68
+ "types": "./dist/radio.d.ts",
69
+ "import": "./dist/radio.js"
70
+ },
71
+ "./range": {
72
+ "types": "./dist/range.d.ts",
73
+ "import": "./dist/range.js"
74
+ },
75
+ "./select": {
76
+ "types": "./dist/select.d.ts",
77
+ "import": "./dist/select.js"
78
+ },
79
+ "./split": {
80
+ "types": "./dist/split.d.ts",
81
+ "import": "./dist/split.js"
82
+ },
83
+ "./tabs": {
84
+ "types": "./dist/tabs.d.ts",
85
+ "import": "./dist/tabs.js"
86
+ },
87
+ "./tooltip": {
88
+ "types": "./dist/tooltip.d.ts",
89
+ "import": "./dist/tooltip.js"
90
+ },
91
+ "./tree": {
92
+ "types": "./dist/tree.d.ts",
93
+ "import": "./dist/tree.js"
94
+ },
95
+ "./themes/light.css": "./themes/haiyue-light.css",
96
+ "./themes/dark.css": "./themes/haiyue-dark.css",
97
+ "./package.json": "./package.json"
26
98
  },
99
+ "engines": { "node": ">=22" },
27
100
  "publishConfig": {
28
101
  "access": "public",
29
102
  "tag": "latest"
@@ -33,12 +106,15 @@
33
106
  "LICENSE",
34
107
  "dist/**/*.js",
35
108
  "dist/**/*.d.ts",
36
- "src/**/*"
109
+ "themes/*.css"
37
110
  ],
38
111
  "scripts": {
39
112
  "build": "tsc -p tsconfig.json",
113
+ "examples": "npm run build && node scripts/serve-examples.mjs",
40
114
  "typecheck": "tsc -p tsconfig.json --noEmit",
41
- "test": "npm run build && node --test \"test/**/*.test.mjs\""
115
+ "test": "npm run build && node --test \"test/**/*.test.mjs\"",
116
+ "prepack": "npm run typecheck && npm test",
117
+ "pack:candidate": "node scripts/pack-candidate.mjs"
42
118
  },
43
119
  "devDependencies": {
44
120
  "typescript": "^5.2.0"
@@ -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
+ }