@nuraly/lumenjs 0.1.3 → 0.2.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 (306) hide show
  1. package/README.md +62 -282
  2. package/dist/auth/config.d.ts +23 -0
  3. package/dist/auth/config.js +115 -0
  4. package/dist/auth/guard.d.ts +12 -0
  5. package/dist/auth/guard.js +28 -0
  6. package/dist/auth/index.d.ts +3 -0
  7. package/dist/auth/index.js +1 -0
  8. package/dist/auth/middleware.d.ts +23 -0
  9. package/dist/auth/middleware.js +89 -0
  10. package/dist/auth/native-auth.d.ts +82 -0
  11. package/dist/auth/native-auth.js +340 -0
  12. package/dist/auth/oidc-client.d.ts +17 -0
  13. package/dist/auth/oidc-client.js +123 -0
  14. package/dist/auth/providers/google.d.ts +23 -0
  15. package/dist/auth/providers/google.js +25 -0
  16. package/dist/auth/providers/index.d.ts +2 -0
  17. package/dist/auth/providers/index.js +1 -0
  18. package/dist/auth/routes/login.d.ts +8 -0
  19. package/dist/auth/routes/login.js +121 -0
  20. package/dist/auth/routes/logout.d.ts +4 -0
  21. package/dist/auth/routes/logout.js +79 -0
  22. package/dist/auth/routes/oidc-callback.d.ts +3 -0
  23. package/dist/auth/routes/oidc-callback.js +70 -0
  24. package/dist/auth/routes/password.d.ts +5 -0
  25. package/dist/auth/routes/password.js +149 -0
  26. package/dist/auth/routes/signup.d.ts +3 -0
  27. package/dist/auth/routes/signup.js +81 -0
  28. package/dist/auth/routes/token.d.ts +4 -0
  29. package/dist/auth/routes/token.js +70 -0
  30. package/dist/auth/routes/totp.d.ts +22 -0
  31. package/dist/auth/routes/totp.js +232 -0
  32. package/dist/auth/routes/utils.d.ts +7 -0
  33. package/dist/auth/routes/utils.js +35 -0
  34. package/dist/auth/routes/verify.d.ts +3 -0
  35. package/dist/auth/routes/verify.js +26 -0
  36. package/dist/auth/routes.d.ts +8 -0
  37. package/dist/auth/routes.js +124 -0
  38. package/dist/auth/session.d.ts +8 -0
  39. package/dist/auth/session.js +54 -0
  40. package/dist/auth/token.d.ts +33 -0
  41. package/dist/auth/token.js +90 -0
  42. package/dist/auth/types.d.ts +156 -0
  43. package/dist/auth/types.js +2 -0
  44. package/dist/build/build-client.d.ts +15 -0
  45. package/dist/build/build-client.js +45 -0
  46. package/dist/build/build-prerender.d.ts +11 -0
  47. package/dist/build/build-prerender.js +159 -0
  48. package/dist/build/build-server.d.ts +18 -0
  49. package/dist/build/build-server.js +107 -0
  50. package/dist/build/build.js +60 -123
  51. package/dist/build/scan.d.ts +18 -0
  52. package/dist/build/scan.js +77 -6
  53. package/dist/build/serve-api.js +8 -2
  54. package/dist/build/serve-loaders.d.ts +4 -4
  55. package/dist/build/serve-loaders.js +26 -18
  56. package/dist/build/serve-ssr.js +38 -11
  57. package/dist/build/serve-static.js +3 -3
  58. package/dist/build/serve.js +341 -18
  59. package/dist/cli.js +37 -6
  60. package/dist/communication/encryption.d.ts +35 -0
  61. package/dist/communication/encryption.js +90 -0
  62. package/dist/communication/handlers/context.d.ts +27 -0
  63. package/dist/communication/handlers/context.js +1 -0
  64. package/dist/communication/handlers/conversation.d.ts +24 -0
  65. package/dist/communication/handlers/conversation.js +113 -0
  66. package/dist/communication/handlers/file-upload.d.ts +17 -0
  67. package/dist/communication/handlers/file-upload.js +62 -0
  68. package/dist/communication/handlers/messaging.d.ts +30 -0
  69. package/dist/communication/handlers/messaging.js +237 -0
  70. package/dist/communication/handlers/presence.d.ts +15 -0
  71. package/dist/communication/handlers/presence.js +76 -0
  72. package/dist/communication/handlers.d.ts +5 -0
  73. package/dist/communication/handlers.js +5 -0
  74. package/dist/communication/index.d.ts +9 -0
  75. package/dist/communication/index.js +7 -0
  76. package/dist/communication/link-preview.d.ts +18 -0
  77. package/dist/communication/link-preview.js +115 -0
  78. package/dist/communication/schema.d.ts +10 -0
  79. package/dist/communication/schema.js +101 -0
  80. package/dist/communication/server.d.ts +86 -0
  81. package/dist/communication/server.js +212 -0
  82. package/dist/communication/signaling.d.ts +43 -0
  83. package/dist/communication/signaling.js +271 -0
  84. package/dist/communication/store.d.ts +71 -0
  85. package/dist/communication/store.js +289 -0
  86. package/dist/communication/types.d.ts +454 -0
  87. package/dist/communication/types.js +1 -0
  88. package/dist/create.d.ts +1 -0
  89. package/dist/create.js +55 -0
  90. package/dist/db/auto-migrate.d.ts +3 -0
  91. package/dist/db/auto-migrate.js +100 -0
  92. package/dist/db/client.d.ts +3 -0
  93. package/dist/db/client.js +18 -0
  94. package/dist/db/index.d.ts +17 -13
  95. package/dist/db/index.js +205 -26
  96. package/dist/db/seed.d.ts +12 -0
  97. package/dist/db/seed.js +88 -0
  98. package/dist/db/table.d.ts +10 -0
  99. package/dist/db/table.js +12 -0
  100. package/dist/dev-server/config.d.ts +11 -0
  101. package/dist/dev-server/config.js +40 -20
  102. package/dist/dev-server/index-html.d.ts +4 -0
  103. package/dist/dev-server/index-html.js +21 -6
  104. package/dist/dev-server/nuralyui-aliases.d.ts +0 -4
  105. package/dist/dev-server/nuralyui-aliases.js +115 -94
  106. package/dist/dev-server/plugins/vite-plugin-api-routes.js +29 -5
  107. package/dist/dev-server/plugins/vite-plugin-auth.d.ts +6 -0
  108. package/dist/dev-server/plugins/vite-plugin-auth.js +223 -0
  109. package/dist/dev-server/plugins/vite-plugin-auto-define.d.ts +16 -0
  110. package/dist/dev-server/plugins/vite-plugin-auto-define.js +111 -0
  111. package/dist/dev-server/plugins/vite-plugin-communication.d.ts +6 -0
  112. package/dist/dev-server/plugins/vite-plugin-communication.js +205 -0
  113. package/dist/dev-server/plugins/vite-plugin-editor-api.d.ts +6 -0
  114. package/dist/dev-server/plugins/vite-plugin-editor-api.js +318 -0
  115. package/dist/dev-server/plugins/vite-plugin-i18n.js +69 -2
  116. package/dist/dev-server/plugins/vite-plugin-lit-dedup.d.ts +6 -0
  117. package/dist/dev-server/plugins/vite-plugin-lit-dedup.js +78 -34
  118. package/dist/dev-server/plugins/vite-plugin-lit-hmr.js +44 -2
  119. package/dist/dev-server/plugins/vite-plugin-llms.d.ts +2 -0
  120. package/dist/dev-server/plugins/vite-plugin-llms.js +92 -0
  121. package/dist/dev-server/plugins/vite-plugin-loaders.js +146 -13
  122. package/dist/dev-server/plugins/vite-plugin-routes.js +16 -5
  123. package/dist/dev-server/plugins/vite-plugin-socketio.d.ts +2 -0
  124. package/dist/dev-server/plugins/vite-plugin-socketio.js +51 -0
  125. package/dist/dev-server/plugins/vite-plugin-source-annotator.d.ts +2 -0
  126. package/dist/dev-server/plugins/vite-plugin-source-annotator.js +26 -3
  127. package/dist/dev-server/plugins/vite-plugin-storage.d.ts +10 -0
  128. package/dist/dev-server/plugins/vite-plugin-storage.js +126 -0
  129. package/dist/dev-server/plugins/vite-plugin-virtual-modules.js +140 -3
  130. package/dist/dev-server/server.js +242 -70
  131. package/dist/dev-server/ssr-render.d.ts +2 -1
  132. package/dist/dev-server/ssr-render.js +117 -50
  133. package/dist/editor/ai/backend.d.ts +20 -0
  134. package/dist/editor/ai/backend.js +113 -0
  135. package/dist/editor/ai/claude-code-client.d.ts +20 -0
  136. package/dist/editor/ai/claude-code-client.js +145 -0
  137. package/dist/editor/ai/deepseek-client.d.ts +7 -0
  138. package/dist/editor/ai/deepseek-client.js +113 -0
  139. package/dist/editor/ai/opencode-client.d.ts +14 -0
  140. package/dist/editor/ai/opencode-client.js +99 -0
  141. package/dist/editor/ai/snapshot-store.d.ts +22 -0
  142. package/dist/editor/ai/snapshot-store.js +35 -0
  143. package/dist/editor/ai/types.d.ts +30 -0
  144. package/dist/editor/ai/types.js +136 -0
  145. package/dist/editor/ai-chat-panel.d.ts +13 -0
  146. package/dist/editor/ai-chat-panel.js +613 -0
  147. package/dist/editor/ai-markdown.d.ts +10 -0
  148. package/dist/editor/ai-markdown.js +70 -0
  149. package/dist/editor/ai-project-panel.d.ts +11 -0
  150. package/dist/editor/ai-project-panel.js +332 -0
  151. package/dist/editor/ast-modification.d.ts +11 -0
  152. package/dist/editor/ast-modification.js +1 -0
  153. package/dist/editor/ast-service.d.ts +30 -0
  154. package/dist/editor/ast-service.js +180 -0
  155. package/dist/editor/css-rules.d.ts +54 -0
  156. package/dist/editor/css-rules.js +423 -0
  157. package/dist/editor/editor-api-client.d.ts +51 -0
  158. package/dist/editor/editor-api-client.js +162 -0
  159. package/dist/editor/editor-bridge.d.ts +1 -0
  160. package/dist/editor/editor-bridge.js +18 -8
  161. package/dist/editor/editor-toolbar.d.ts +14 -0
  162. package/dist/editor/editor-toolbar.js +115 -0
  163. package/dist/editor/file-editor.d.ts +9 -0
  164. package/dist/editor/file-editor.js +236 -0
  165. package/dist/editor/file-service.d.ts +16 -0
  166. package/dist/editor/file-service.js +52 -0
  167. package/dist/editor/i18n-key-gen.d.ts +1 -0
  168. package/dist/editor/i18n-key-gen.js +7 -0
  169. package/dist/editor/inline-text-edit.d.ts +5 -0
  170. package/dist/editor/inline-text-edit.js +173 -92
  171. package/dist/editor/overlay-events.d.ts +5 -0
  172. package/dist/editor/overlay-events.js +364 -0
  173. package/dist/editor/overlay-hmr.d.ts +2 -0
  174. package/dist/editor/overlay-hmr.js +76 -0
  175. package/dist/editor/overlay-selection.d.ts +29 -0
  176. package/dist/editor/overlay-selection.js +148 -0
  177. package/dist/editor/overlay-utils.d.ts +12 -0
  178. package/dist/editor/overlay-utils.js +59 -0
  179. package/dist/editor/properties-panel-persist.d.ts +14 -0
  180. package/dist/editor/properties-panel-persist.js +70 -0
  181. package/dist/editor/properties-panel-rows.d.ts +10 -0
  182. package/dist/editor/properties-panel-rows.js +349 -0
  183. package/dist/editor/properties-panel-styles.d.ts +4 -0
  184. package/dist/editor/properties-panel-styles.js +174 -0
  185. package/dist/editor/properties-panel.d.ts +4 -0
  186. package/dist/editor/properties-panel.js +148 -0
  187. package/dist/editor/property-registry.d.ts +16 -0
  188. package/dist/editor/property-registry.js +303 -0
  189. package/dist/editor/standalone-file-panel.d.ts +0 -0
  190. package/dist/editor/standalone-file-panel.js +1 -0
  191. package/dist/editor/standalone-overlay-dom.d.ts +0 -0
  192. package/dist/editor/standalone-overlay-dom.js +1 -0
  193. package/dist/editor/standalone-overlay-styles.d.ts +0 -0
  194. package/dist/editor/standalone-overlay-styles.js +1 -0
  195. package/dist/editor/standalone-overlay.d.ts +1 -0
  196. package/dist/editor/standalone-overlay.js +76 -0
  197. package/dist/editor/syntax-highlighter.d.ts +4 -0
  198. package/dist/editor/syntax-highlighter.js +81 -0
  199. package/dist/editor/text-toolbar.d.ts +11 -0
  200. package/dist/editor/text-toolbar.js +327 -0
  201. package/dist/editor/toolbar-styles.d.ts +4 -0
  202. package/dist/editor/toolbar-styles.js +198 -0
  203. package/dist/email/index.d.ts +32 -0
  204. package/dist/email/index.js +154 -0
  205. package/dist/email/providers/resend.d.ts +2 -0
  206. package/dist/email/providers/resend.js +24 -0
  207. package/dist/email/providers/sendgrid.d.ts +2 -0
  208. package/dist/email/providers/sendgrid.js +31 -0
  209. package/dist/email/providers/smtp.d.ts +13 -0
  210. package/dist/email/providers/smtp.js +125 -0
  211. package/dist/email/template-engine.d.ts +18 -0
  212. package/dist/email/template-engine.js +116 -0
  213. package/dist/email/templates/base.d.ts +9 -0
  214. package/dist/email/templates/base.js +65 -0
  215. package/dist/email/templates/password-reset.d.ts +5 -0
  216. package/dist/email/templates/password-reset.js +15 -0
  217. package/dist/email/templates/verify-email.d.ts +5 -0
  218. package/dist/email/templates/verify-email.js +15 -0
  219. package/dist/email/templates/welcome.d.ts +5 -0
  220. package/dist/email/templates/welcome.js +13 -0
  221. package/dist/email/types.d.ts +49 -0
  222. package/dist/email/types.js +1 -0
  223. package/dist/llms/generate.d.ts +46 -0
  224. package/dist/llms/generate.js +185 -0
  225. package/dist/permissions/guard.d.ts +28 -0
  226. package/dist/permissions/guard.js +30 -0
  227. package/dist/permissions/index.d.ts +6 -0
  228. package/dist/permissions/index.js +3 -0
  229. package/dist/permissions/service.d.ts +80 -0
  230. package/dist/permissions/service.js +210 -0
  231. package/dist/permissions/tables.d.ts +5 -0
  232. package/dist/permissions/tables.js +68 -0
  233. package/dist/permissions/types.d.ts +33 -0
  234. package/dist/permissions/types.js +1 -0
  235. package/dist/runtime/app-shell.d.ts +1 -1
  236. package/dist/runtime/app-shell.js +164 -0
  237. package/dist/runtime/auth.d.ts +10 -0
  238. package/dist/runtime/auth.js +30 -0
  239. package/dist/runtime/communication.d.ts +137 -0
  240. package/dist/runtime/communication.js +228 -0
  241. package/dist/runtime/error-boundary.d.ts +23 -0
  242. package/dist/runtime/error-boundary.js +120 -0
  243. package/dist/runtime/i18n.d.ts +6 -1
  244. package/dist/runtime/i18n.js +42 -21
  245. package/dist/runtime/island.d.ts +16 -0
  246. package/dist/runtime/island.js +80 -0
  247. package/dist/runtime/router-data.d.ts +3 -0
  248. package/dist/runtime/router-data.js +102 -17
  249. package/dist/runtime/router-hydration.js +34 -2
  250. package/dist/runtime/router.d.ts +19 -2
  251. package/dist/runtime/router.js +237 -43
  252. package/dist/runtime/socket-client.d.ts +2 -0
  253. package/dist/runtime/socket-client.js +30 -0
  254. package/dist/runtime/webrtc.d.ts +91 -0
  255. package/dist/runtime/webrtc.js +428 -0
  256. package/dist/shared/dom-shims.js +4 -2
  257. package/dist/shared/graceful-shutdown.d.ts +8 -0
  258. package/dist/shared/graceful-shutdown.js +36 -0
  259. package/dist/shared/health.d.ts +8 -0
  260. package/dist/shared/health.js +25 -0
  261. package/dist/shared/llms-txt.d.ts +31 -0
  262. package/dist/shared/llms-txt.js +85 -0
  263. package/dist/shared/logger.d.ts +32 -0
  264. package/dist/shared/logger.js +93 -0
  265. package/dist/shared/meta.d.ts +27 -0
  266. package/dist/shared/meta.js +71 -0
  267. package/dist/shared/middleware-runner.d.ts +9 -0
  268. package/dist/shared/middleware-runner.js +29 -0
  269. package/dist/shared/rate-limit.d.ts +18 -0
  270. package/dist/shared/rate-limit.js +71 -0
  271. package/dist/shared/request-id.d.ts +5 -0
  272. package/dist/shared/request-id.js +18 -0
  273. package/dist/shared/route-matching.js +16 -1
  274. package/dist/shared/security-headers.d.ts +18 -0
  275. package/dist/shared/security-headers.js +38 -0
  276. package/dist/shared/socket-io-setup.d.ts +11 -0
  277. package/dist/shared/socket-io-setup.js +51 -0
  278. package/dist/shared/types.d.ts +15 -0
  279. package/dist/shared/utils.d.ts +33 -7
  280. package/dist/shared/utils.js +164 -27
  281. package/dist/storage/adapters/local.d.ts +44 -0
  282. package/dist/storage/adapters/local.js +85 -0
  283. package/dist/storage/adapters/s3.d.ts +32 -0
  284. package/dist/storage/adapters/s3.js +119 -0
  285. package/dist/storage/adapters/types.d.ts +53 -0
  286. package/dist/storage/adapters/types.js +1 -0
  287. package/dist/storage/index.d.ts +76 -0
  288. package/dist/storage/index.js +83 -0
  289. package/package.json +45 -7
  290. package/templates/blog/api/posts.ts +4 -18
  291. package/templates/blog/data/migrations/001_init.sql +6 -5
  292. package/templates/blog/lumenjs.config.ts +3 -0
  293. package/templates/blog/package.json +14 -0
  294. package/templates/blog/pages/_layout.ts +25 -0
  295. package/templates/blog/pages/index.ts +48 -22
  296. package/templates/blog/pages/posts/[slug].ts +45 -20
  297. package/templates/blog/pages/tag/[tag].ts +44 -0
  298. package/templates/dashboard/api/stats.ts +8 -5
  299. package/templates/dashboard/lumenjs.config.ts +3 -0
  300. package/templates/dashboard/package.json +14 -0
  301. package/templates/dashboard/pages/_layout.ts +25 -0
  302. package/templates/dashboard/pages/index.ts +54 -23
  303. package/templates/dashboard/pages/settings/index.ts +29 -0
  304. package/templates/default/lumenjs.config.ts +3 -0
  305. package/templates/default/package.json +14 -0
  306. package/templates/default/pages/index.ts +24 -0
@@ -0,0 +1,11 @@
1
+ /**
2
+ * AI Project Panel — right-side sliding chat panel for project-wide conversations.
3
+ * For things like "add a new page", "set up auth", "change the color scheme everywhere".
4
+ * Streams responses from OpenCode AI coding agent via the editor API.
5
+ */
6
+ export declare function createAiProjectPanel(): HTMLDivElement;
7
+ export declare function showAiProjectPanel(): void;
8
+ export declare function hideAiProjectPanel(): void;
9
+ export declare function isAiProjectPanelOpen(): boolean;
10
+ /** Send a message programmatically (e.g. from the toolbar page AI input) */
11
+ export declare function sendProjectMessage(text: string): void;
@@ -0,0 +1,332 @@
1
+ /**
2
+ * AI Project Panel — right-side sliding chat panel for project-wide conversations.
3
+ * For things like "add a new page", "set up auth", "change the color scheme everywhere".
4
+ * Streams responses from OpenCode AI coding agent via the editor API.
5
+ */
6
+ import { streamAiChat, rollbackAiTurn, checkAiStatus } from './editor-api-client.js';
7
+ import { renderMarkdown } from './ai-markdown.js';
8
+ const PROJECT_SUGGESTIONS = [
9
+ { label: 'Add a page', prompt: 'Add a new page to the project' },
10
+ { label: 'Add API route', prompt: 'Add a new API route' },
11
+ { label: 'Dark theme', prompt: 'Add a dark theme to the entire project' },
12
+ { label: 'Add auth', prompt: 'Set up authentication for the project' },
13
+ { label: 'Add i18n', prompt: 'Add internationalization support' },
14
+ { label: 'Improve SEO', prompt: 'Improve SEO across all pages' },
15
+ ];
16
+ let panel;
17
+ let messagesContainer;
18
+ let inputEl;
19
+ let sendBtn;
20
+ let sessionId;
21
+ let activeController = null;
22
+ let aiConfigured = false;
23
+ export function createAiProjectPanel() {
24
+ panel = document.createElement('div');
25
+ panel.id = 'nk-ai-project';
26
+ panel.innerHTML = `
27
+ <div class="nk-aip-header">
28
+ <span class="nk-aip-title">✦ Project AI</span>
29
+ <div style="flex:1"></div>
30
+ <button class="nk-aip-close" title="Close">✕</button>
31
+ </div>
32
+ <div class="nk-aip-messages"></div>
33
+ <div class="nk-aip-suggestions">
34
+ ${PROJECT_SUGGESTIONS.map(s => `<button class="nk-aip-chip" data-prompt="${s.prompt.replace(/"/g, '&quot;')}">${s.label}</button>`).join('')}
35
+ </div>
36
+ <div class="nk-aip-input-row">
37
+ <textarea class="nk-aip-input" placeholder="Ask about your project..." rows="1"></textarea>
38
+ <button class="nk-aip-send" disabled title="Send">▶</button>
39
+ </div>
40
+ `;
41
+ const style = document.createElement('style');
42
+ style.textContent = `
43
+ #nk-ai-project {
44
+ position: fixed; top: 44px; right: 0; bottom: 0; width: 380px;
45
+ display: none; flex-direction: column;
46
+ background: #1e1b2e; color: #e2e8f0; font-family: system-ui, -apple-system, sans-serif;
47
+ font-size: 13px; z-index: 99999;
48
+ border-left: 1px solid #334155;
49
+ box-shadow: -4px 0 20px rgba(0,0,0,0.4);
50
+ transition: transform 0.2s ease;
51
+ }
52
+ #nk-ai-project.open { display: flex; }
53
+ .nk-aip-header {
54
+ display: flex; align-items: center; gap: 8px;
55
+ padding: 0 14px; height: 42px; min-height: 42px;
56
+ border-bottom: 1px solid #334155;
57
+ }
58
+ .nk-aip-title { font-weight: 700; font-size: 13px; color: #7c3aed; }
59
+ .nk-aip-close {
60
+ background: transparent; border: none; color: #94a3b8; cursor: pointer;
61
+ font-size: 16px; padding: 4px 8px; border-radius: 4px;
62
+ font-family: inherit; line-height: 1;
63
+ }
64
+ .nk-aip-close:hover { background: #334155; color: #e2e8f0; }
65
+ .nk-aip-messages {
66
+ flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 10px;
67
+ }
68
+ .nk-aip-messages:empty::after {
69
+ content: 'Ask me anything about your project — add pages, set up features, change styles across the whole app.';
70
+ color: #64748b; font-size: 12px; line-height: 1.6; text-align: center;
71
+ padding: 40px 20px;
72
+ }
73
+ .nk-aip-msg {
74
+ max-width: 88%; padding: 8px 12px; border-radius: 12px; font-size: 13px;
75
+ line-height: 1.5; word-wrap: break-word;
76
+ }
77
+ .nk-aip-msg.user {
78
+ align-self: flex-end; background: #7c3aed; color: #fff; border-bottom-right-radius: 4px;
79
+ }
80
+ .nk-aip-msg.assistant {
81
+ align-self: flex-start; background: #2d2a3e; color: #e2e8f0; border-bottom-left-radius: 4px;
82
+ }
83
+ .nk-aip-msg.assistant p { margin: 0 0 6px 0; }
84
+ .nk-aip-msg.assistant p:last-child { margin-bottom: 0; }
85
+ .nk-aip-msg.assistant ul, .nk-aip-msg.assistant ol { margin: 4px 0; padding-left: 18px; }
86
+ .nk-aip-msg.assistant li { margin: 2px 0; }
87
+ .nk-aip-msg.assistant strong { font-weight: 600; }
88
+ .nk-ai-code {
89
+ background: #1a1a2e; padding: 1px 4px; border-radius: 3px;
90
+ font-family: 'SF Mono', ui-monospace, monospace; font-size: 11px;
91
+ }
92
+ .nk-ai-pre {
93
+ background: #1a1a2e; padding: 8px; border-radius: 6px;
94
+ overflow-x: auto; margin: 4px 0; position: relative;
95
+ }
96
+ .nk-ai-pre code {
97
+ font-family: 'SF Mono', ui-monospace, monospace; font-size: 11px;
98
+ background: none; padding: 0; white-space: pre;
99
+ }
100
+ .nk-ai-code-lang {
101
+ position: absolute; top: 4px; right: 6px;
102
+ font-size: 9px; color: #64748b; font-family: system-ui, sans-serif;
103
+ }
104
+ .nk-aip-typing {
105
+ align-self: flex-start; padding: 8px 16px; background: #2d2a3e; border-radius: 12px;
106
+ border-bottom-left-radius: 4px;
107
+ }
108
+ .nk-aip-typing span {
109
+ display: inline-block; width: 6px; height: 6px; background: #94a3b8;
110
+ border-radius: 50%; margin: 0 2px; animation: nk-aip-dot 1.2s infinite;
111
+ }
112
+ .nk-aip-typing span:nth-child(2) { animation-delay: 0.2s; }
113
+ .nk-aip-typing span:nth-child(3) { animation-delay: 0.4s; }
114
+ @keyframes nk-aip-dot {
115
+ 0%, 80%, 100% { opacity: 0.3; transform: scale(0.8); }
116
+ 40% { opacity: 1; transform: scale(1); }
117
+ }
118
+ .nk-aip-suggestions {
119
+ display: flex; flex-wrap: wrap; gap: 6px; padding: 10px 14px;
120
+ border-top: 1px solid #334155;
121
+ }
122
+ .nk-aip-chip {
123
+ background: transparent; border: 1px solid #334155; border-radius: 14px;
124
+ padding: 5px 12px; color: #94a3b8; cursor: pointer; font-size: 11px;
125
+ white-space: nowrap; font-family: inherit; transition: all 0.15s;
126
+ }
127
+ .nk-aip-chip:hover { border-color: #7c3aed; color: #e2e8f0; }
128
+ .nk-aip-input-row {
129
+ display: flex; gap: 8px; padding: 10px 14px;
130
+ border-top: 1px solid #334155; align-items: flex-end;
131
+ }
132
+ .nk-aip-input {
133
+ flex: 1; background: #2d2a3e; border: 1px solid #334155; border-radius: 8px;
134
+ color: #e2e8f0; font-size: 13px; padding: 8px 12px;
135
+ font-family: inherit; resize: none; max-height: 80px; overflow-y: auto;
136
+ line-height: 1.4; outline: none;
137
+ }
138
+ .nk-aip-input::placeholder { color: #64748b; }
139
+ .nk-aip-input:focus { border-color: #7c3aed; }
140
+ .nk-aip-send {
141
+ background: #7c3aed; border: none; color: #fff; width: 34px; height: 34px;
142
+ border-radius: 8px; cursor: pointer; font-size: 13px;
143
+ display: flex; align-items: center; justify-content: center;
144
+ flex-shrink: 0; transition: opacity 0.15s;
145
+ }
146
+ .nk-aip-send:disabled { opacity: 0.4; cursor: default; }
147
+ .nk-aip-send:not(:disabled):hover { background: #6d28d9; }
148
+ .nk-aip-msg-actions {
149
+ display: flex; align-items: center; gap: 6px; margin-top: 6px; font-size: 11px;
150
+ }
151
+ .nk-aip-badge {
152
+ background: #334155; color: #94a3b8; padding: 2px 8px; border-radius: 8px;
153
+ }
154
+ .nk-aip-rollback {
155
+ background: transparent; border: none; color: #f59e0b; cursor: pointer;
156
+ font-size: 11px; padding: 2px 4px; font-family: inherit;
157
+ }
158
+ .nk-aip-rollback:hover { text-decoration: underline; }
159
+ @media (max-width: 640px) {
160
+ #nk-ai-project { width: 100%; }
161
+ }
162
+ `;
163
+ panel.appendChild(style);
164
+ document.body.appendChild(panel);
165
+ messagesContainer = panel.querySelector('.nk-aip-messages');
166
+ inputEl = panel.querySelector('.nk-aip-input');
167
+ sendBtn = panel.querySelector('.nk-aip-send');
168
+ // Close
169
+ panel.querySelector('.nk-aip-close').addEventListener('click', () => hideAiProjectPanel());
170
+ // Send
171
+ sendBtn.addEventListener('click', () => sendMessage());
172
+ // Input auto-resize + enable/disable
173
+ inputEl.addEventListener('input', () => {
174
+ inputEl.style.height = 'auto';
175
+ inputEl.style.height = Math.min(inputEl.scrollHeight, 80) + 'px';
176
+ sendBtn.disabled = !inputEl.value.trim();
177
+ });
178
+ // Enter to send
179
+ inputEl.addEventListener('keydown', (e) => {
180
+ if (e.key === 'Enter' && !e.shiftKey) {
181
+ e.preventDefault();
182
+ if (inputEl.value.trim())
183
+ sendMessage();
184
+ }
185
+ });
186
+ // Suggestion chips
187
+ panel.querySelectorAll('.nk-aip-chip').forEach((btn) => {
188
+ btn.addEventListener('click', () => {
189
+ const prompt = btn.dataset.prompt || '';
190
+ inputEl.value = prompt;
191
+ sendBtn.disabled = false;
192
+ sendMessage();
193
+ });
194
+ });
195
+ // Check AI status on creation
196
+ checkAiStatus().then(status => {
197
+ aiConfigured = status.configured;
198
+ }).catch(() => {
199
+ aiConfigured = false;
200
+ });
201
+ return panel;
202
+ }
203
+ export function showAiProjectPanel() {
204
+ panel.classList.add('open');
205
+ inputEl.focus();
206
+ }
207
+ export function hideAiProjectPanel() {
208
+ panel.classList.remove('open');
209
+ }
210
+ export function isAiProjectPanelOpen() {
211
+ return panel.classList.contains('open');
212
+ }
213
+ /** Send a message programmatically (e.g. from the toolbar page AI input) */
214
+ export function sendProjectMessage(text) {
215
+ inputEl.value = text;
216
+ sendMessage();
217
+ }
218
+ function addUserMessage(text) {
219
+ const msg = document.createElement('div');
220
+ msg.className = 'nk-aip-msg user';
221
+ msg.textContent = text;
222
+ messagesContainer.appendChild(msg);
223
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
224
+ }
225
+ function createStreamingMessage() {
226
+ const msg = document.createElement('div');
227
+ msg.className = 'nk-aip-msg assistant';
228
+ const textEl = document.createElement('div');
229
+ textEl.className = 'nk-aip-msg-text';
230
+ msg.appendChild(textEl);
231
+ messagesContainer.appendChild(msg);
232
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
233
+ return msg;
234
+ }
235
+ function finalizeAssistantMessage(msg, turnId) {
236
+ if (turnId) {
237
+ // Remove rollback from previous messages
238
+ messagesContainer.querySelectorAll('.nk-aip-msg.assistant .nk-aip-rollback').forEach((btn) => {
239
+ if (!msg.contains(btn))
240
+ btn.style.display = 'none';
241
+ });
242
+ const actions = document.createElement('div');
243
+ actions.className = 'nk-aip-msg-actions';
244
+ actions.innerHTML = `
245
+ <span class="nk-aip-badge">Changes applied</span>
246
+ <button class="nk-aip-rollback">↩ Rollback</button>
247
+ `;
248
+ actions.querySelector('.nk-aip-rollback').addEventListener('click', async () => {
249
+ try {
250
+ await rollbackAiTurn(turnId);
251
+ const badge = actions.querySelector('.nk-aip-badge');
252
+ if (badge) {
253
+ badge.textContent = 'Rolled back';
254
+ badge.style.color = '#f59e0b';
255
+ }
256
+ const btn = actions.querySelector('.nk-aip-rollback');
257
+ if (btn)
258
+ btn.style.display = 'none';
259
+ }
260
+ catch (err) {
261
+ console.error('[ai-project] Rollback failed:', err);
262
+ }
263
+ });
264
+ msg.appendChild(actions);
265
+ }
266
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
267
+ }
268
+ function addAssistantError(message) {
269
+ const msg = document.createElement('div');
270
+ msg.className = 'nk-aip-msg assistant';
271
+ msg.style.borderColor = '#ef4444';
272
+ const textEl = document.createElement('div');
273
+ textEl.className = 'nk-aip-msg-text';
274
+ textEl.textContent = message;
275
+ textEl.style.color = '#fca5a5';
276
+ msg.appendChild(textEl);
277
+ messagesContainer.appendChild(msg);
278
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
279
+ }
280
+ function sendMessage() {
281
+ const text = inputEl.value.trim();
282
+ if (!text)
283
+ return;
284
+ if (!aiConfigured) {
285
+ addUserMessage(text);
286
+ inputEl.value = '';
287
+ inputEl.style.height = 'auto';
288
+ sendBtn.disabled = true;
289
+ addAssistantError('AI not available — start OpenCode server to enable AI coding.');
290
+ return;
291
+ }
292
+ // Abort any active request
293
+ if (activeController) {
294
+ activeController.abort();
295
+ activeController = null;
296
+ }
297
+ addUserMessage(text);
298
+ inputEl.value = '';
299
+ inputEl.style.height = 'auto';
300
+ sendBtn.disabled = true;
301
+ // Typing indicator
302
+ const typing = document.createElement('div');
303
+ typing.className = 'nk-aip-typing';
304
+ typing.innerHTML = '<span></span><span></span><span></span>';
305
+ messagesContainer.appendChild(typing);
306
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
307
+ const streamMsg = createStreamingMessage();
308
+ const textEl = streamMsg.querySelector('.nk-aip-msg-text');
309
+ let rawText = '';
310
+ activeController = streamAiChat('project', text, {}, sessionId, {
311
+ onToken: (token) => {
312
+ typing.remove();
313
+ rawText += token;
314
+ textEl.innerHTML = renderMarkdown(rawText);
315
+ messagesContainer.scrollTop = messagesContainer.scrollHeight;
316
+ },
317
+ onDone: (result) => {
318
+ typing.remove();
319
+ sessionId = result.sessionId;
320
+ const finalText = rawText || result.fullText || 'Done.';
321
+ textEl.innerHTML = renderMarkdown(finalText);
322
+ finalizeAssistantMessage(streamMsg, result.turnId);
323
+ activeController = null;
324
+ },
325
+ onError: (message) => {
326
+ typing.remove();
327
+ streamMsg.remove();
328
+ addAssistantError(message);
329
+ activeController = null;
330
+ },
331
+ });
332
+ }
@@ -0,0 +1,11 @@
1
+ export interface AstModification {
2
+ type: 'setAttribute' | 'removeAttribute' | 'insertElement' | 'removeElement' | 'setTextContent';
3
+ elementSelector: string;
4
+ /** Source line number from data-nk-source — used to find the exact element in the template */
5
+ sourceLine?: number;
6
+ attributeName?: string;
7
+ attributeValue?: string;
8
+ parentSelector?: string;
9
+ position?: 'before' | 'after' | 'firstChild' | 'lastChild';
10
+ html?: string;
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ import { AstModification } from './ast-modification.js';
2
+ /**
3
+ * AST Service — parses TypeScript Lit components and applies modifications
4
+ * to the html`` tagged template literals.
5
+ *
6
+ * Uses ts-morph to locate the render() method's template literal,
7
+ * then applies string-level HTML modifications.
8
+ */
9
+ export declare class AstService {
10
+ /**
11
+ * Apply an AST modification to a TypeScript source file.
12
+ * Returns the modified source string.
13
+ */
14
+ applyModification(sourceCode: string, modification: AstModification): Promise<string>;
15
+ private applyHtmlModification;
16
+ /**
17
+ * Find an element's open tag in the template HTML.
18
+ * If sourceLine is provided, finds the occurrence at that specific source line.
19
+ * Otherwise falls back to first match by tag/selector.
20
+ */
21
+ private findElement;
22
+ private setAttribute;
23
+ private removeAttribute;
24
+ private setTextContent;
25
+ private insertElement;
26
+ private removeElement;
27
+ private parseSelector;
28
+ private buildOpenTagRegex;
29
+ private escapeRegex;
30
+ }
@@ -0,0 +1,180 @@
1
+ /**
2
+ * AST Service — parses TypeScript Lit components and applies modifications
3
+ * to the html`` tagged template literals.
4
+ *
5
+ * Uses ts-morph to locate the render() method's template literal,
6
+ * then applies string-level HTML modifications.
7
+ */
8
+ export class AstService {
9
+ /**
10
+ * Apply an AST modification to a TypeScript source file.
11
+ * Returns the modified source string.
12
+ */
13
+ async applyModification(sourceCode, modification) {
14
+ // Dynamic import — ts-morph is an optional peer dependency.
15
+ // Use a variable to prevent TypeScript from resolving the module at compile time.
16
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
17
+ let tsMorph;
18
+ const moduleName = 'ts-morph';
19
+ try {
20
+ tsMorph = await import(moduleName);
21
+ }
22
+ catch {
23
+ throw new Error('ts-morph is required for AST modifications. Install it with: npm install ts-morph');
24
+ }
25
+ const Project = tsMorph.Project;
26
+ const SyntaxKind = tsMorph.SyntaxKind;
27
+ const project = new Project({ useInMemoryFileSystem: true });
28
+ const sourceFile = project.createSourceFile('temp.ts', sourceCode);
29
+ // Find the render() method
30
+ const renderMethod = sourceFile.getDescendantsOfKind(SyntaxKind.MethodDeclaration)
31
+ .find((m) => m.getName() === 'render');
32
+ if (!renderMethod) {
33
+ throw new Error('No render() method found in source file');
34
+ }
35
+ // Find the html tagged template expression inside render()
36
+ const taggedTemplates = renderMethod.getDescendantsOfKind(SyntaxKind.TaggedTemplateExpression);
37
+ const htmlTemplate = taggedTemplates.find((t) => t.getTag().getText() === 'html');
38
+ if (!htmlTemplate) {
39
+ throw new Error('No html`` tagged template found in render()');
40
+ }
41
+ const template = htmlTemplate.getTemplate();
42
+ const templateStart = template.getStart() + 1; // skip opening backtick
43
+ const templateEnd = template.getEnd() - 1; // skip closing backtick
44
+ const templateText = sourceCode.substring(templateStart, templateEnd);
45
+ // Calculate the line number where the template starts in the source file
46
+ const templateStartLine = sourceCode.substring(0, templateStart).split('\n').length;
47
+ const modifiedHtml = this.applyHtmlModification(templateText, modification, templateStartLine);
48
+ // Reconstruct the source with the modified template
49
+ const fullText = sourceFile.getFullText();
50
+ return fullText.substring(0, templateStart) + modifiedHtml + fullText.substring(templateEnd);
51
+ }
52
+ applyHtmlModification(html, mod, templateStartLine) {
53
+ switch (mod.type) {
54
+ case 'setAttribute':
55
+ return this.setAttribute(html, mod.elementSelector, mod.attributeName, mod.attributeValue, mod.sourceLine, templateStartLine);
56
+ case 'removeAttribute':
57
+ return this.removeAttribute(html, mod.elementSelector, mod.attributeName, mod.sourceLine, templateStartLine);
58
+ case 'setTextContent':
59
+ return this.setTextContent(html, mod.elementSelector, mod.html || '', mod.sourceLine, templateStartLine);
60
+ case 'insertElement':
61
+ return this.insertElement(html, mod.parentSelector || mod.elementSelector, mod.position || 'lastChild', mod.html || '', mod.sourceLine, templateStartLine);
62
+ case 'removeElement':
63
+ return this.removeElement(html, mod.elementSelector, mod.sourceLine, templateStartLine);
64
+ default:
65
+ throw new Error(`Unsupported modification type: ${mod.type}`);
66
+ }
67
+ }
68
+ /**
69
+ * Find an element's open tag in the template HTML.
70
+ * If sourceLine is provided, finds the occurrence at that specific source line.
71
+ * Otherwise falls back to first match by tag/selector.
72
+ */
73
+ findElement(html, selector, sourceLine, templateStartLine) {
74
+ const { tag, attrFilter } = this.parseSelector(selector);
75
+ const openTagRegex = this.buildOpenTagRegex(tag, attrFilter);
76
+ // If we have a source line, find the occurrence at that line
77
+ if (sourceLine && templateStartLine) {
78
+ const targetLineInTemplate = sourceLine - templateStartLine;
79
+ let m;
80
+ const globalRegex = new RegExp(openTagRegex.source, openTagRegex.flags.includes('g') ? openTagRegex.flags : openTagRegex.flags + 'g');
81
+ while ((m = globalRegex.exec(html)) !== null) {
82
+ const linesBeforeMatch = html.substring(0, m.index).split('\n').length - 1;
83
+ // Allow ±1 line tolerance for minor shifts
84
+ if (Math.abs(linesBeforeMatch - targetLineInTemplate) <= 1) {
85
+ return { match: m, tag };
86
+ }
87
+ }
88
+ // Fall through to first-match if line-based match fails
89
+ }
90
+ const match = openTagRegex.exec(html);
91
+ if (!match)
92
+ throw new Error(`Element not found: ${selector}`);
93
+ return { match, tag };
94
+ }
95
+ setAttribute(html, selector, attrName, attrValue, sourceLine, templateStartLine) {
96
+ const { match } = this.findElement(html, selector, sourceLine, templateStartLine);
97
+ const openTag = match[0];
98
+ const existingAttrRegex = new RegExp(`(\\s)${this.escapeRegex(attrName)}\\s*=\\s*("(?:[^"$]|\\$\\{[^}]*\\})*"|'(?:[^'$]|\\$\\{[^}]*\\})*'|\\$\\{[^}]*\\}|\\S+)`);
99
+ const existingMatch = existingAttrRegex.exec(openTag);
100
+ let newOpenTag;
101
+ if (existingMatch) {
102
+ newOpenTag = openTag.replace(existingAttrRegex, `$1${attrName}="${attrValue}"`);
103
+ }
104
+ else {
105
+ newOpenTag = openTag.replace(/(\/?>)$/, ` ${attrName}="${attrValue}"$1`);
106
+ }
107
+ return html.substring(0, match.index) + newOpenTag + html.substring(match.index + openTag.length);
108
+ }
109
+ removeAttribute(html, selector, attrName, sourceLine, templateStartLine) {
110
+ const { match } = this.findElement(html, selector, sourceLine, templateStartLine);
111
+ const openTag = match[0];
112
+ const attrRegex = new RegExp(`\\s+${this.escapeRegex(attrName)}\\s*=\\s*("[^"]*"|'[^']*'|\\S+)`, 'g');
113
+ const newOpenTag = openTag.replace(attrRegex, '');
114
+ return html.substring(0, match.index) + newOpenTag + html.substring(match.index + openTag.length);
115
+ }
116
+ setTextContent(html, selector, text, sourceLine, templateStartLine) {
117
+ const { match, tag } = this.findElement(html, selector, sourceLine, templateStartLine);
118
+ const afterOpenTag = match.index + match[0].length;
119
+ const closeTag = `</${tag}>`;
120
+ const closeIndex = html.indexOf(closeTag, afterOpenTag);
121
+ if (closeIndex === -1)
122
+ throw new Error(`Closing tag not found for: ${tag}`);
123
+ return html.substring(0, afterOpenTag) + text + html.substring(closeIndex);
124
+ }
125
+ insertElement(html, parentSelector, position, newHtml, sourceLine, templateStartLine) {
126
+ const { match, tag } = this.findElement(html, parentSelector, sourceLine, templateStartLine);
127
+ const closeTag = `</${tag}>`;
128
+ const closeIndex = html.indexOf(closeTag, match.index + match[0].length);
129
+ if (closeIndex === -1)
130
+ throw new Error(`Closing tag not found for: ${tag}`);
131
+ switch (position) {
132
+ case 'firstChild': {
133
+ const insertAt = match.index + match[0].length;
134
+ return html.substring(0, insertAt) + '\n ' + newHtml + html.substring(insertAt);
135
+ }
136
+ case 'lastChild': {
137
+ return html.substring(0, closeIndex) + ' ' + newHtml + '\n ' + html.substring(closeIndex);
138
+ }
139
+ case 'before': {
140
+ return html.substring(0, match.index) + newHtml + '\n ' + html.substring(match.index);
141
+ }
142
+ case 'after': {
143
+ const afterClose = closeIndex + closeTag.length;
144
+ return html.substring(0, afterClose) + '\n ' + newHtml + html.substring(afterClose);
145
+ }
146
+ default:
147
+ throw new Error(`Unknown position: ${position}`);
148
+ }
149
+ }
150
+ removeElement(html, selector, sourceLine, templateStartLine) {
151
+ const { match, tag } = this.findElement(html, selector, sourceLine, templateStartLine);
152
+ // Check if self-closing
153
+ if (match[0].endsWith('/>')) {
154
+ return html.substring(0, match.index) + html.substring(match.index + match[0].length);
155
+ }
156
+ const closeTag = `</${tag}>`;
157
+ const closeIndex = html.indexOf(closeTag, match.index + match[0].length);
158
+ if (closeIndex === -1) {
159
+ return html.substring(0, match.index) + html.substring(match.index + match[0].length);
160
+ }
161
+ return html.substring(0, match.index) + html.substring(closeIndex + closeTag.length);
162
+ }
163
+ parseSelector(selector) {
164
+ const bracketMatch = selector.match(/^([a-zA-Z][a-zA-Z0-9-]*)\[([a-zA-Z][a-zA-Z0-9-]*)=['"]([^'"]+)['"]\]$/);
165
+ if (bracketMatch) {
166
+ return { tag: bracketMatch[1], attrFilter: { name: bracketMatch[2], value: bracketMatch[3] } };
167
+ }
168
+ return { tag: selector };
169
+ }
170
+ buildOpenTagRegex(tag, attrFilter) {
171
+ const attrContent = '(?:[^>$]|\\$\\{[^}]*\\})*';
172
+ if (attrFilter) {
173
+ return new RegExp(`<${this.escapeRegex(tag)}\\s${attrContent}${this.escapeRegex(attrFilter.name)}\\s*=\\s*["']${this.escapeRegex(attrFilter.value)}["']${attrContent}\\/?>`, 's');
174
+ }
175
+ return new RegExp(`<${this.escapeRegex(tag)}(\\s${attrContent})?\\/?>`, 's');
176
+ }
177
+ escapeRegex(str) {
178
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
179
+ }
180
+ }
@@ -0,0 +1,54 @@
1
+ /** CSS style properties with known enum values */
2
+ export declare const CSS_ENUMS: Record<string, string[]>;
3
+ /** Common CSS properties for the "add style" autocomplete */
4
+ export declare const COMMON_CSS_PROPS: string[];
5
+ export interface CssRule {
6
+ selector: string;
7
+ properties: [string, string][];
8
+ /** Character offset of this rule's opening brace in the css`` content */
9
+ startOffset: number;
10
+ /** Character offset of this rule's closing brace in the css`` content */
11
+ endOffset: number;
12
+ }
13
+ export interface ExtractedCss {
14
+ cssContent: string;
15
+ cssStart: number;
16
+ rules: CssRule[];
17
+ }
18
+ /**
19
+ * Find the source file for the host custom element (the one with static styles).
20
+ * Walks up from the selected element through shadow DOM to find the host.
21
+ */
22
+ export declare function findHostSourceFile(element: HTMLElement): string | null;
23
+ /**
24
+ * Extract CSS rules from a `static styles = css\`...\`` block in the source.
25
+ */
26
+ export declare function extractCssFromSource(source: string): ExtractedCss | null;
27
+ /**
28
+ * Simple CSS rule parser — extracts selector + properties from a CSS string.
29
+ * Handles nested @media by flattening rules inside.
30
+ */
31
+ export declare function parseCssRules(css: string): CssRule[];
32
+ /**
33
+ * Check if a CSS selector matches the selected element.
34
+ */
35
+ export declare function selectorMatchesElement(selector: string, element: HTMLElement): boolean;
36
+ export declare function isColorValue(name: string, value: string): boolean;
37
+ export declare function normalizeToHex(value: string): string;
38
+ /** Notify the overlay system that element layout may have changed */
39
+ export declare function notifyLayoutChange(): void;
40
+ /**
41
+ * Render a CSS rule as a collapsible section in the panel.
42
+ */
43
+ export declare function renderCssRule(rule: CssRule, extracted: ExtractedCss, fullSource: string, sourceFile: string, container: HTMLDivElement, startOpen: boolean, currentElementRef: {
44
+ current: HTMLElement | null;
45
+ }, debounceTimers: Record<string, ReturnType<typeof setTimeout>>): void;
46
+ export declare function renderAllRules(rules: CssRule[], extracted: ExtractedCss, fullSource: string, sourceFile: string, container: HTMLDivElement, currentElementRef: {
47
+ current: HTMLElement | null;
48
+ }, debounceTimers: Record<string, ReturnType<typeof setTimeout>>): void;
49
+ /**
50
+ * Load CSS rules from the component source file and render matching rules in the panel.
51
+ */
52
+ export declare function loadCssRulesForElement(element: HTMLElement, cssGroup: HTMLDivElement, currentElementRef: {
53
+ current: HTMLElement | null;
54
+ }, debounceTimers: Record<string, ReturnType<typeof setTimeout>>): Promise<void>;