@kupola/kupola 1.2.0 → 1.4.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 (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +330 -286
  3. package/adapters/axios.d.ts +34 -0
  4. package/adapters/axios.js +122 -0
  5. package/adapters/navios-http.d.ts +110 -0
  6. package/adapters/navios-http.js +151 -0
  7. package/css/components-ext.css +157 -0
  8. package/css/components.css +69 -0
  9. package/css/theme-dark.css +5 -5
  10. package/css/utilities.css +119 -1
  11. package/dist/css/accessibility.css +119 -0
  12. package/dist/css/animations.css +224 -0
  13. package/dist/css/brand-themes.css +300 -0
  14. package/dist/css/colors_and_type.css +441 -0
  15. package/dist/css/components-ext.css +4165 -0
  16. package/dist/css/components.css +1483 -0
  17. package/dist/css/kupola.css +1 -12
  18. package/dist/css/kupola.min.css +1 -0
  19. package/dist/css/responsive.css +697 -0
  20. package/dist/css/scaffold.css +145 -0
  21. package/dist/css/states.css +316 -0
  22. package/dist/css/theme-dark.css +296 -0
  23. package/dist/css/theme-light.css +296 -0
  24. package/dist/css/utilities.css +171 -0
  25. package/dist/icons.svg +284 -0
  26. package/dist/kupola.cjs.js +17409 -257
  27. package/dist/kupola.cjs.js.map +1 -1
  28. package/dist/kupola.css +9000 -0
  29. package/dist/kupola.esm.js +17183 -7597
  30. package/dist/kupola.esm.js.map +1 -1
  31. package/dist/kupola.min.css +1 -0
  32. package/dist/kupola.min.js +2 -0
  33. package/dist/kupola.min.js.map +1 -0
  34. package/dist/kupola.umd.js +17415 -257
  35. package/dist/kupola.umd.js.map +1 -1
  36. package/dist/plugins/vite-plugin-kupola.js +120 -0
  37. package/dist/types/kupola.d.ts +421 -323
  38. package/js/calendar.js +334 -25
  39. package/js/carousel.js +182 -48
  40. package/js/collapse.js +148 -34
  41. package/js/color-picker.js +416 -108
  42. package/js/component.js +8 -19
  43. package/js/countdown.js +9 -8
  44. package/js/data-bind.js +73 -16
  45. package/js/datepicker.js +488 -110
  46. package/js/depends.js +710 -0
  47. package/js/dialog.js +4 -2
  48. package/js/drawer.js +172 -8
  49. package/js/dropdown.js +272 -17
  50. package/js/dynamic-tags.js +156 -40
  51. package/js/fileupload.js +9 -8
  52. package/js/form.js +280 -254
  53. package/js/global-events.js +281 -188
  54. package/js/heatmap.js +10 -7
  55. package/js/i18n.js +18 -10
  56. package/js/icons.js +141 -161
  57. package/js/image-preview.js +146 -2
  58. package/js/initializer.js +151 -71
  59. package/js/kupola-core.js +123 -45
  60. package/js/kupola-lifecycle.js +13 -11
  61. package/js/message.js +8 -1
  62. package/js/modal.js +207 -59
  63. package/js/notification.js +8 -1
  64. package/js/numberinput.js +9 -8
  65. package/js/pagination.js +263 -0
  66. package/js/registry.js +29 -12
  67. package/js/select.js +482 -27
  68. package/js/slide-captcha.js +11 -2
  69. package/js/slider.js +442 -25
  70. package/js/statcard.js +9 -7
  71. package/js/table.js +1210 -0
  72. package/js/tag.js +268 -14
  73. package/js/theme-standalone.js +88 -0
  74. package/js/theme.js +14 -43
  75. package/js/timepicker.js +335 -66
  76. package/js/tooltip.js +317 -86
  77. package/js/utils.js +6 -2
  78. package/js/validation.js +6 -2
  79. package/js/virtual-list.js +11 -7
  80. package/js/web-components.js +288 -0
  81. package/package.json +81 -67
  82. package/plugins/vite-plugin-kupola.js +120 -0
  83. package/scripts/build-css.cjs +113 -0
  84. package/scripts/build-svg-sprite.cjs +107 -0
  85. package/types/kupola.d.ts +421 -323
  86. package/CHANGELOG.md +0 -130
  87. package/INTEGRATION.md +0 -440
  88. package/PROJECT_SUMMARY.md +0 -312
  89. package/SKILL.md +0 -572
  90. package/dist/utils/utils/Kupola.cs +0 -77
  91. package/dist/utils/utils/Kupola.java +0 -104
  92. package/dist/utils/utils/kupola.go +0 -120
  93. package/dist/utils/utils/kupola.js +0 -63
  94. package/dist/utils/utils/kupola.py +0 -1392
  95. package/dist/utils/utils/kupola.rb +0 -69
  96. package/js/composition-api.js +0 -458
  97. package/js/error-handler.js +0 -181
  98. package/js/http.js +0 -419
  99. package/js/kupola-devtools.js +0 -598
  100. package/js/performance.js +0 -250
  101. package/js/router.js +0 -396
  102. package/js/security.js +0 -189
  103. package/js/test-utils.js +0 -251
  104. package/templates/base.html +0 -30
  105. package/templates/base_dashboard.html +0 -99
  106. package/utils/Kupola.cs +0 -77
  107. package/utils/Kupola.java +0 -104
  108. package/utils/kupola.go +0 -120
  109. package/utils/kupola.js +0 -63
  110. package/utils/kupola.py +0 -1392
  111. package/utils/kupola.rb +0 -69
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Kupola Web Components — Custom Elements wrapper for core Kupola components.
3
+ *
4
+ * Usage:
5
+ * import { registerWebComponents } from '@kupola/kupola';
6
+ * registerWebComponents();
7
+ *
8
+ * Then in HTML:
9
+ * <k-dropdown>
10
+ * <button slot="trigger">Open</button>
11
+ * <a slot="item">Option A</a>
12
+ * <a slot="item">Option B</a>
13
+ * </k-dropdown>
14
+ *
15
+ * <k-tooltip title="Hello!">
16
+ * <button>Hover me</button>
17
+ * </k-tooltip>
18
+ *
19
+ * <k-collapse>
20
+ * <k-collapse-item title="Section 1">Content 1</k-collapse-item>
21
+ * <k-collapse-item title="Section 2">Content 2</k-collapse-item>
22
+ * </k-collapse>
23
+ */
24
+
25
+ let registered = false;
26
+
27
+ /**
28
+ * <k-dropdown> — Wraps Kupola dropdown
29
+ * Slots: trigger (button), item (menu items)
30
+ */
31
+ class KupolaDropdownElement extends HTMLElement {
32
+ static get observedAttributes() { return ['open']; }
33
+
34
+ connectedCallback() {
35
+ this._render();
36
+ }
37
+
38
+ _render() {
39
+ const trigger = this.querySelector('[slot="trigger"]');
40
+ const items = this.querySelectorAll('[slot="item"]');
41
+
42
+ // Build dropdown DOM structure
43
+ const wrapper = document.createElement('div');
44
+ wrapper.className = 'ds-dropdown';
45
+ wrapper.setAttribute('data-dropdown', '');
46
+
47
+ if (trigger) {
48
+ trigger.setAttribute('class', (trigger.getAttribute('class') || '') + ' ds-dropdown__trigger');
49
+ wrapper.appendChild(trigger);
50
+ }
51
+
52
+ const menu = document.createElement('div');
53
+ menu.className = 'ds-dropdown__menu';
54
+ items.forEach(item => {
55
+ item.className = 'ds-dropdown__item';
56
+ menu.appendChild(item);
57
+ });
58
+ wrapper.appendChild(menu);
59
+
60
+ this.innerHTML = '';
61
+ this.appendChild(wrapper);
62
+ }
63
+
64
+ attributeChangedCallback(name, oldVal, newVal) {
65
+ if (name === 'open') {
66
+ const menu = this.querySelector('.ds-dropdown__menu');
67
+ if (menu) menu.style.display = newVal !== null ? 'block' : '';
68
+ }
69
+ }
70
+ }
71
+
72
+ /**
73
+ * <k-tooltip title="text"> — Wraps Kupola tooltip
74
+ * Default slot: the trigger element
75
+ */
76
+ class KupolaTooltipElement extends HTMLElement {
77
+ static get observedAttributes() { return ['title', 'position']; }
78
+
79
+ connectedCallback() {
80
+ const trigger = this.firstElementChild;
81
+ if (trigger) {
82
+ trigger.setAttribute('data-title', this.getAttribute('title') || '');
83
+ if (this.getAttribute('position')) {
84
+ trigger.setAttribute('data-tooltip-position', this.getAttribute('position'));
85
+ }
86
+ }
87
+ }
88
+
89
+ attributeChangedCallback(name, oldVal, newVal) {
90
+ if (name === 'title') {
91
+ const trigger = this.firstElementChild;
92
+ if (trigger) trigger.setAttribute('data-title', newVal || '');
93
+ }
94
+ }
95
+ }
96
+
97
+ /**
98
+ * <k-collapse> — Wraps Kupola collapse/accordion
99
+ * Children: <k-collapse-item title="...">content</k-collapse-item>
100
+ */
101
+ class KupolaCollapseElement extends HTMLElement {
102
+ connectedCallback() {
103
+ this._render();
104
+ }
105
+
106
+ _render() {
107
+ const wrapper = document.createElement('div');
108
+ wrapper.className = 'ds-collapse';
109
+ wrapper.setAttribute('data-collapse', '');
110
+
111
+ const items = this.querySelectorAll('k-collapse-item');
112
+ items.forEach(item => {
113
+ const title = item.getAttribute('title') || '';
114
+ const content = item.innerHTML;
115
+
116
+ const collapseItem = document.createElement('div');
117
+ collapseItem.className = 'ds-collapse__item';
118
+ collapseItem.innerHTML = `
119
+ <button class="ds-collapse__header">
120
+ <span>${title}</span>
121
+ <svg class="icon ds-collapse__chevron" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"/></svg>
122
+ </button>
123
+ <div class="ds-collapse__body"><div class="ds-collapse__content">${content}</div></div>
124
+ `;
125
+ wrapper.appendChild(collapseItem);
126
+ });
127
+
128
+ this.innerHTML = '';
129
+ this.appendChild(wrapper);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * <k-collapse-item title="..."> — Child of <k-collapse>
135
+ * Not registered as a standalone custom element, just a data container.
136
+ */
137
+ class KupolaCollapseItemElement extends HTMLElement {
138
+ static get observedAttributes() { return ['title']; }
139
+ // Content is read by parent <k-collapse> during its connectedCallback
140
+ }
141
+
142
+ /**
143
+ * <k-drawer> — Wraps Kupola drawer
144
+ * Attributes: position (left|right|top|bottom), open
145
+ */
146
+ class KupolaDrawerElement extends HTMLElement {
147
+ static get observedAttributes() { return ['position', 'open']; }
148
+
149
+ connectedCallback() {
150
+ this._render();
151
+ }
152
+
153
+ _render() {
154
+ const position = this.getAttribute('position') || 'left';
155
+ const wrapper = document.createElement('div');
156
+ wrapper.className = `ds-drawer ds-drawer--${position}`;
157
+ wrapper.setAttribute('data-drawer', '');
158
+ wrapper.innerHTML = this.innerHTML;
159
+ this.innerHTML = '';
160
+ this.appendChild(wrapper);
161
+ }
162
+
163
+ attributeChangedCallback(name, oldVal, newVal) {
164
+ if (name === 'open') {
165
+ const drawer = this.querySelector('.ds-drawer');
166
+ if (drawer) {
167
+ drawer.classList.toggle('is-open', newVal !== null);
168
+ }
169
+ }
170
+ }
171
+ }
172
+
173
+ /**
174
+ * <k-modal> — Wraps Kupola modal dialog
175
+ * Attributes: title, open
176
+ */
177
+ class KupolaModalElement extends HTMLElement {
178
+ static get observedAttributes() { return ['title', 'open']; }
179
+
180
+ connectedCallback() {
181
+ this._render();
182
+ }
183
+
184
+ _render() {
185
+ const title = this.getAttribute('title') || '';
186
+ const wrapper = document.createElement('div');
187
+ wrapper.className = 'ds-backdrop';
188
+ wrapper.style.display = 'none';
189
+ wrapper.innerHTML = `
190
+ <div class="ds-dialog">
191
+ <div class="ds-dialog__head">
192
+ <span class="ds-dialog__title">${title}</span>
193
+ <button class="ds-dialog__close" aria-label="Close">
194
+ <svg class="icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
195
+ </button>
196
+ </div>
197
+ <div class="ds-dialog__body"></div>
198
+ <div class="ds-dialog__foot"></div>
199
+ </div>
200
+ `;
201
+
202
+ // Move slotted content into dialog body
203
+ const body = wrapper.querySelector('.ds-dialog__body');
204
+ const content = this.querySelector('[slot="body"]');
205
+ if (content) {
206
+ body.appendChild(content);
207
+ }
208
+
209
+ // Move footer content
210
+ const foot = wrapper.querySelector('.ds-dialog__foot');
211
+ const footer = this.querySelector('[slot="footer"]');
212
+ if (footer) {
213
+ foot.appendChild(footer);
214
+ }
215
+
216
+ // Close button handler
217
+ const closeBtn = wrapper.querySelector('.ds-dialog__close');
218
+ if (closeBtn) {
219
+ closeBtn.addEventListener('click', () => this.close());
220
+ }
221
+ // Backdrop click handler
222
+ wrapper.addEventListener('click', (e) => {
223
+ if (e.target === wrapper) this.close();
224
+ });
225
+
226
+ this.innerHTML = '';
227
+ this.appendChild(wrapper);
228
+ }
229
+
230
+ attributeChangedCallback(name, oldVal, newVal) {
231
+ if (name === 'open') {
232
+ const backdrop = this.querySelector('.ds-backdrop');
233
+ if (backdrop) {
234
+ backdrop.style.display = newVal !== null ? 'flex' : 'none';
235
+ }
236
+ }
237
+ }
238
+
239
+ open() {
240
+ this.setAttribute('open', '');
241
+ }
242
+
243
+ close() {
244
+ this.removeAttribute('open');
245
+ }
246
+ }
247
+
248
+ /**
249
+ * Register all Kupola Web Components.
250
+ * Safe to call multiple times — will only register once.
251
+ */
252
+ export function registerWebComponents() {
253
+ if (registered || typeof customElements === 'undefined') return;
254
+ registered = true;
255
+
256
+ const components = [
257
+ ['k-dropdown', KupolaDropdownElement],
258
+ ['k-tooltip', KupolaTooltipElement],
259
+ ['k-collapse', KupolaCollapseElement],
260
+ ['k-collapse-item', KupolaCollapseItemElement],
261
+ ['k-drawer', KupolaDrawerElement],
262
+ ['k-modal', KupolaModalElement]
263
+ ];
264
+
265
+ for (const [tag, cls] of components) {
266
+ if (!customElements.get(tag)) {
267
+ customElements.define(tag, cls);
268
+ }
269
+ }
270
+ }
271
+
272
+ export {
273
+ KupolaDropdownElement,
274
+ KupolaTooltipElement,
275
+ KupolaCollapseElement,
276
+ KupolaCollapseItemElement,
277
+ KupolaDrawerElement,
278
+ KupolaModalElement
279
+ };
280
+
281
+ if (typeof window !== 'undefined') {
282
+ window.registerWebComponents = registerWebComponents;
283
+ window.KupolaDropdownElement = KupolaDropdownElement;
284
+ window.KupolaTooltipElement = KupolaTooltipElement;
285
+ window.KupolaCollapseElement = KupolaCollapseElement;
286
+ window.KupolaDrawerElement = KupolaDrawerElement;
287
+ window.KupolaModalElement = KupolaModalElement;
288
+ }
package/package.json CHANGED
@@ -1,105 +1,119 @@
1
1
  {
2
2
  "name": "@kupola/kupola",
3
- "version": "1.2.0",
4
- "description": "Lightweight UI toolkit for any web project. No heavy frontend frameworks required. Works seamlessly with Flask, Django, Gin, Spring Boot, ASP.NET Core, Express, Ruby on Rails, and any backend framework that outputs HTML.",
5
- "type": "module",
3
+ "version": "1.4.0",
4
+ "description": "A lightweight UI toolkit for any web project. No heavy frontend frameworks required.",
6
5
  "main": "dist/kupola.cjs.js",
7
6
  "module": "dist/kupola.esm.js",
8
7
  "types": "dist/types/kupola.d.ts",
8
+ "type": "module",
9
+ "sideEffects": false,
10
+ "engines": {
11
+ "node": ">=16.0.0"
12
+ },
9
13
  "exports": {
10
14
  ".": {
11
15
  "import": "./dist/kupola.esm.js",
12
16
  "require": "./dist/kupola.cjs.js",
13
17
  "types": "./dist/types/kupola.d.ts"
14
18
  },
15
- "./css": {
16
- "import": "./dist/css/kupola.css",
17
- "require": "./dist/css/kupola.css"
19
+ "./depends": {
20
+ "import": "./js/depends.js",
21
+ "types": "./dist/types/kupola.d.ts"
22
+ },
23
+ "./component": {
24
+ "import": "./js/kupola-core.js"
25
+ },
26
+ "./data-bind": {
27
+ "import": "./js/data-bind.js"
28
+ },
29
+ "./theme": {
30
+ "import": "./js/theme.js"
31
+ },
32
+ "./icons": {
33
+ "import": "./js/icons.js"
34
+ },
35
+ "./validation": {
36
+ "import": "./js/validation.js"
37
+ },
38
+ "./vite": {
39
+ "import": "./plugins/vite-plugin-kupola.js"
40
+ },
41
+ "./adapters/axios": {
42
+ "import": "./adapters/axios.js",
43
+ "types": "./adapters/axios.d.ts"
18
44
  },
19
- "./icons": "./icons/",
20
- "./js/": "./js/",
21
- "./css/": "./css/"
45
+ "./adapters/navios-http": {
46
+ "import": "./adapters/navios-http.js",
47
+ "types": "./adapters/navios-http.d.ts"
48
+ },
49
+ "./dist/css/kupola.css": "./dist/css/kupola.css",
50
+ "./dist/css/*": "./dist/css/*",
51
+ "./dist/icons/*": "./dist/icons/*"
22
52
  },
23
53
  "scripts": {
54
+ "dev": "vite",
24
55
  "build": "vite build",
25
- "prepublishOnly": "npm run build",
26
56
  "build:rollup": "rollup -c",
27
- "watch:rollup": "rollup -c -w",
28
- "watch:vite": "vite build --watch",
29
- "dev": "vite",
30
- "start": "npx http-server -p 8080",
57
+ "lint": "eslint .",
58
+ "lint:fix": "eslint . --fix",
59
+ "format": "prettier --write .",
60
+ "build:css": "node scripts/build-css.cjs",
61
+ "build:sprite": "node scripts/build-svg-sprite.cjs",
62
+ "build:all": "node scripts/build-css.cjs && node scripts/build-svg-sprite.cjs",
31
63
  "test": "jest",
32
64
  "test:watch": "jest --watch",
33
- "test:coverage": "jest --coverage",
34
- "lint": "eslint js/ src/",
35
- "lint:fix": "eslint js/ src/ --fix",
36
- "format": "prettier --write js/ src/ css/"
65
+ "test:coverage": "jest --coverage"
37
66
  },
38
67
  "keywords": [
39
68
  "ui",
40
69
  "components",
41
- "ui-components",
42
70
  "design-system",
71
+ "frontend",
43
72
  "vanilla-js",
44
73
  "no-framework",
45
74
  "lightweight",
46
- "web-components",
47
- "frontend",
48
- "css-framework",
49
- "javascript-ui",
50
- "form-validation",
75
+ "ssr",
76
+ "server-side-rendering",
77
+ "declarative",
51
78
  "data-binding",
79
+ "htmx-alternative",
80
+ "alpinejs-alternative",
52
81
  "responsive",
53
82
  "dark-theme",
54
- "accessibility",
55
- "flask",
56
- "django",
57
- "python",
58
- "gin",
59
- "go",
60
- "spring-boot",
61
- "java",
62
- "aspnetcore",
63
- "csharp",
64
- "express",
65
- "nodejs",
66
- "rails",
67
- "ruby",
68
- "backend-agnostic",
69
- "html-css-js"
83
+ "http-client",
84
+ "axios-adapter",
85
+ "typescript"
70
86
  ],
71
87
  "author": "kupola-cn",
72
88
  "license": "MIT",
89
+ "repository": {
90
+ "type": "git",
91
+ "url": "https://github.com/kupola-cn/kupola"
92
+ },
93
+ "bugs": {
94
+ "url": "https://github.com/kupola-cn/kupola/issues"
95
+ },
96
+ "homepage": "https://github.com/kupola-cn/kupola",
73
97
  "files": [
74
- "dist",
75
- "js",
76
- "css",
77
- "icons",
78
- "utils",
79
- "types",
80
- "templates",
81
- "README.md",
82
- "SKILL.md",
83
- "INTEGRATION.md",
84
- "CHANGELOG.md",
85
- "PROJECT_SUMMARY.md"
98
+ "dist/",
99
+ "js/",
100
+ "css/",
101
+ "icons/",
102
+ "plugins/",
103
+ "adapters/",
104
+ "types/",
105
+ "scripts/"
86
106
  ],
87
107
  "devDependencies": {
88
- "@rollup/plugin-commonjs": "^29.0.3",
89
- "@rollup/plugin-node-resolve": "^16.0.3",
90
- "@rollup/plugin-terser": "^1.0.0",
91
- "clean-css": "^5.3.3",
92
- "rollup": "^4.62.2",
108
+ "@rollup/plugin-commonjs": "^25.0.7",
109
+ "@rollup/plugin-node-resolve": "^15.2.3",
110
+ "@rollup/plugin-terser": "^0.4.4",
111
+ "eslint": "^8.57.0",
112
+ "jest": "^29.7.0",
113
+ "prettier": "^3.2.5",
114
+ "rollup": "^4.13.0",
93
115
  "rollup-plugin-copy": "^3.5.0",
94
116
  "rollup-plugin-postcss": "^4.0.2",
95
- "terser": "^5.31.0",
96
- "vite": "^5.4.1",
97
- "eslint": "^9.5.0",
98
- "prettier": "^3.3.2",
99
- "jest": "^29.7.0",
100
- "jest-environment-jsdom": "^29.7.0"
101
- },
102
- "dependencies": {
103
- "ws": "^8.21.0"
117
+ "vite": "^5.1.6"
104
118
  }
105
- }
119
+ }
@@ -0,0 +1,120 @@
1
+ /**
2
+ * @kupola/vite-plugin - Vite plugin for Kupola development
3
+ *
4
+ * Features:
5
+ * - Auto-import useDeps/useQuery when detected in code
6
+ * - Auto-inject Kupola CSS
7
+ * - Dev-time dependency graph visualization
8
+ * - Transform helper for @depends-like syntax
9
+ */
10
+
11
+ import { readFileSync } from 'fs';
12
+ import { resolve } from 'path';
13
+
14
+ /**
15
+ * @param {Object} options
16
+ * @param {boolean} [options.autoCSS=true] - Auto inject Kupola CSS
17
+ * @param {boolean} [options.autoImport=true] - Auto import useDeps/useQuery
18
+ * @param {boolean} [options.devTools=true] - Enable dev tools in dev mode
19
+ * @param {string} [options.cssPath] - Custom CSS path
20
+ */
21
+ export function kupola(options = {}) {
22
+ const {
23
+ autoCSS = true,
24
+ autoImport = true,
25
+ devTools = true,
26
+ cssPath = null
27
+ } = options;
28
+
29
+ let isDev = false;
30
+ let kupolaRoot = null;
31
+
32
+ return {
33
+ name: 'kupola',
34
+ enforce: 'pre',
35
+
36
+ configResolved(config) {
37
+ isDev = config.command === 'serve';
38
+ kupolaRoot = config.root;
39
+ },
40
+
41
+ // Auto-import useDeps/useQuery
42
+ transform(code, id) {
43
+ if (!autoImport) return null;
44
+ if (!id.endsWith('.js') && !id.endsWith('.ts') && !id.endsWith('.vue') && !id.endsWith('.jsx') && !id.endsWith('.tsx')) {
45
+ return null;
46
+ }
47
+
48
+ // Skip node_modules
49
+ if (id.includes('node_modules')) return null;
50
+
51
+ let transformed = code;
52
+ let hasChanges = false;
53
+
54
+ // Detect useDeps usage without import
55
+ if (code.includes('useDeps(') && !code.includes("from '@kupola/kupola'") && !code.includes("from 'kupola'") && !code.includes('from "../js/depends.js"') && !code.includes('from "./js/depends.js"')) {
56
+ const importStatement = `import { useDeps } from '@kupola/kupola';\n`;
57
+ transformed = importStatement + transformed;
58
+ hasChanges = true;
59
+ }
60
+
61
+ // Detect useQuery usage without import
62
+ if (code.includes('useQuery(') && !code.includes("from '@kupola/kupola'") && !code.includes("from 'kupola'") && !code.includes('from "../js/depends.js"') && !code.includes('from "./js/depends.js"')) {
63
+ if (!transformed.includes("import { useQuery }")) {
64
+ const importStatement = `import { useQuery } from '@kupola/kupola';\n`;
65
+ transformed = importStatement + transformed;
66
+ hasChanges = true;
67
+ }
68
+ }
69
+
70
+ if (hasChanges) {
71
+ return {
72
+ code: transformed,
73
+ map: null
74
+ };
75
+ }
76
+
77
+ return null;
78
+ },
79
+
80
+ // Inject CSS in dev mode
81
+ transformIndexHtml(html) {
82
+ if (!autoCSS) return html;
83
+
84
+ const cssLink = cssPath || '/css/kupola.css';
85
+ if (!html.includes(cssLink)) {
86
+ return html.replace(
87
+ '</head>',
88
+ ` <link rel="stylesheet" href="${cssLink}">\n</head>`
89
+ );
90
+ }
91
+ return html;
92
+ },
93
+
94
+ // Dev tools integration
95
+ configureServer(server) {
96
+ if (!devTools || !isDev) return;
97
+
98
+ // Add middleware for dependency graph API
99
+ server.middlewares.use('/__kupola-deps', (req, res) => {
100
+ res.setHeader('Content-Type', 'application/json');
101
+ res.end(JSON.stringify({
102
+ status: 'ok',
103
+ version: '1.2.1',
104
+ features: ['useDeps', 'useQuery', 'Scheduler', 'CacheManager']
105
+ }));
106
+ });
107
+ },
108
+
109
+ // Handle hot update for depends files
110
+ handleHotUpdate(ctx) {
111
+ const { file, modules } = ctx;
112
+ if (file.includes('depends.js')) {
113
+ // Force full reload when core depends files change
114
+ return [];
115
+ }
116
+ }
117
+ };
118
+ }
119
+
120
+ export default kupola;