slicejs-web-framework 3.4.4 → 3.5.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 (110) hide show
  1. package/Slice/Components/Structural/Controller/Controller.js +135 -0
  2. package/Slice/Components/Structural/EventManager/EventManager.js +289 -4
  3. package/Slice/Components/Structural/EventManager/EventManagerDebugger.js +368 -30
  4. package/Slice/Components/Structural/LeakInspector/LeakInspector.js +296 -0
  5. package/Slice/Components/Structural/StylesManager/ThemeManager/ThemeManager.js +56 -0
  6. package/Slice/Slice.js +14 -1
  7. package/Slice/tests/event-emitters.test.js +79 -0
  8. package/Slice/tests/event-registry.test.js +141 -0
  9. package/Slice/tests/leak-orphans.test.js +131 -0
  10. package/api/{middleware → framework}/securityMiddleware.js +18 -74
  11. package/api/framework/server.js +333 -0
  12. package/api/index.js +5 -279
  13. package/api/tests/public-env-resolver.test.js +1 -1
  14. package/package.json +4 -2
  15. package/sliceConfig.schema.json +10 -1
  16. package/src/App/index.html +31 -1
  17. package/src/manifest.json +15 -0
  18. package/src/robots.txt +4 -0
  19. package/src/sitemap.xml +8 -0
  20. package/src/sliceConfig.json +5 -1
  21. package/types/index.d.ts +12 -0
  22. package/slicejs-cli/.github/workflows/ci.yml +0 -43
  23. package/slicejs-cli/LICENSE +0 -21
  24. package/slicejs-cli/assets/Slice.js-logo.png +0 -0
  25. package/slicejs-cli/client.js +0 -740
  26. package/slicejs-cli/commands/Print.js +0 -164
  27. package/slicejs-cli/commands/Validations.js +0 -92
  28. package/slicejs-cli/commands/build/build.js +0 -40
  29. package/slicejs-cli/commands/buildProduction/buildProduction.js +0 -577
  30. package/slicejs-cli/commands/bundle/bundle.js +0 -236
  31. package/slicejs-cli/commands/createComponent/VisualComponentTemplate.js +0 -55
  32. package/slicejs-cli/commands/createComponent/createComponent.js +0 -128
  33. package/slicejs-cli/commands/deleteComponent/deleteComponent.js +0 -81
  34. package/slicejs-cli/commands/doctor/doctor.js +0 -516
  35. package/slicejs-cli/commands/getComponent/getComponent.js +0 -695
  36. package/slicejs-cli/commands/init/init.js +0 -473
  37. package/slicejs-cli/commands/listComponents/listComponents.js +0 -172
  38. package/slicejs-cli/commands/startServer/startServer.js +0 -240
  39. package/slicejs-cli/commands/startServer/watchServer.js +0 -66
  40. package/slicejs-cli/commands/types/types.js +0 -583
  41. package/slicejs-cli/commands/utils/LocalCliDelegation.js +0 -53
  42. package/slicejs-cli/commands/utils/PackageManager.js +0 -148
  43. package/slicejs-cli/commands/utils/PathHelper.js +0 -75
  44. package/slicejs-cli/commands/utils/VersionChecker.js +0 -169
  45. package/slicejs-cli/commands/utils/bundling/BundleGenerator.js +0 -2525
  46. package/slicejs-cli/commands/utils/bundling/DependencyAnalyzer.js +0 -933
  47. package/slicejs-cli/commands/utils/loadConfig.js +0 -31
  48. package/slicejs-cli/commands/utils/sliceScripts.js +0 -48
  49. package/slicejs-cli/commands/utils/updateManager.js +0 -471
  50. package/slicejs-cli/package.json +0 -71
  51. package/slicejs-cli/playwright.config.js +0 -51
  52. package/slicejs-cli/pnpm-lock.yaml +0 -1629
  53. package/slicejs-cli/tests/build-command-integration.test.js +0 -87
  54. package/slicejs-cli/tests/build-mangling-e2e.test.js +0 -290
  55. package/slicejs-cli/tests/build-production-e2e.test.js +0 -140
  56. package/slicejs-cli/tests/builder-edge-cases.test.js +0 -322
  57. package/slicejs-cli/tests/bundle-generate-e2e.test.js +0 -115
  58. package/slicejs-cli/tests/bundle-generator.test.js +0 -691
  59. package/slicejs-cli/tests/bundle-v2-register-output.test.js +0 -470
  60. package/slicejs-cli/tests/bundling-dependency-edges.test.js +0 -127
  61. package/slicejs-cli/tests/bundling-imports-unit.test.js +0 -267
  62. package/slicejs-cli/tests/client-launcher-contract.test.js +0 -211
  63. package/slicejs-cli/tests/commands-component-crud.test.js +0 -128
  64. package/slicejs-cli/tests/commands-doctor.test.js +0 -152
  65. package/slicejs-cli/tests/commands-version-checker.test.js +0 -37
  66. package/slicejs-cli/tests/component-registry-parse.test.js +0 -34
  67. package/slicejs-cli/tests/dependency-analyzer.test.js +0 -24
  68. package/slicejs-cli/tests/e2e/bundles.spec.js +0 -91
  69. package/slicejs-cli/tests/e2e/dependency-scenarios.spec.js +0 -56
  70. package/slicejs-cli/tests/e2e/fixtures/components/Service/FetchManager/FetchManager.js +0 -136
  71. package/slicejs-cli/tests/e2e/fixtures/components/Service/IndexedDbManager/IndexedDbManager.js +0 -149
  72. package/slicejs-cli/tests/e2e/fixtures/components/Service/LocalStorageManager/LocalStorageManager.js +0 -45
  73. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.css +0 -106
  74. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.html +0 -5
  75. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Button/Button.js +0 -158
  76. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Link/Link.js +0 -33
  77. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.css +0 -56
  78. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.html +0 -83
  79. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Loading/Loading.js +0 -164
  80. package/slicejs-cli/tests/e2e/fixtures/components/Visual/MultiRoute/MultiRoute.js +0 -167
  81. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.css +0 -116
  82. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.html +0 -44
  83. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Navbar/Navbar.js +0 -180
  84. package/slicejs-cli/tests/e2e/fixtures/components/Visual/NotFound/NotFound.js +0 -20
  85. package/slicejs-cli/tests/e2e/fixtures/components/Visual/Route/Route.js +0 -181
  86. package/slicejs-cli/tests/e2e/fixtures/components/registry.json +0 -12
  87. package/slicejs-cli/tests/e2e/fixtures/vendor-components.mjs +0 -65
  88. package/slicejs-cli/tests/e2e/navigation.spec.js +0 -44
  89. package/slicejs-cli/tests/e2e/render.spec.js +0 -34
  90. package/slicejs-cli/tests/e2e/serve.mjs +0 -264
  91. package/slicejs-cli/tests/e2e/shared-deps.spec.js +0 -61
  92. package/slicejs-cli/tests/e2e/unminified.spec.js +0 -33
  93. package/slicejs-cli/tests/e2e-serve.test.js +0 -148
  94. package/slicejs-cli/tests/fixtures/components.js +0 -8
  95. package/slicejs-cli/tests/fixtures/sliceConfig.json +0 -74
  96. package/slicejs-cli/tests/getcomponent.test.js +0 -435
  97. package/slicejs-cli/tests/helpers/setup.js +0 -102
  98. package/slicejs-cli/tests/init-command-contract.test.js +0 -101
  99. package/slicejs-cli/tests/init-e2e.test.js +0 -82
  100. package/slicejs-cli/tests/init-project-isolation.test.js +0 -98
  101. package/slicejs-cli/tests/local-cli-delegation.test.js +0 -81
  102. package/slicejs-cli/tests/package-manager.test.js +0 -127
  103. package/slicejs-cli/tests/path-helper.test.js +0 -206
  104. package/slicejs-cli/tests/perf-budget.test.js +0 -84
  105. package/slicejs-cli/tests/postinstall-command.test.js +0 -103
  106. package/slicejs-cli/tests/property-mangling-regression.test.js +0 -182
  107. package/slicejs-cli/tests/types-breakage.test.js +0 -491
  108. package/slicejs-cli/tests/types-generator-errors.test.js +0 -361
  109. package/slicejs-cli/tests/types-generator.test.js +0 -348
  110. /package/api/{utils → framework}/publicEnvResolver.js +0 -0
@@ -22,9 +22,26 @@ export default class EventManagerDebugger extends HTMLElement {
22
22
  this.cacheElements();
23
23
  this.bindEvents();
24
24
  this.makeDraggable();
25
+ this._loadStaticGraph();
25
26
  this.renderList();
26
27
  }
27
28
 
29
+ /**
30
+ * Best-effort load of the generated static event graph (documentation layer).
31
+ * Produced by `slice types generate` at /slice-events.generated.js. Absent =>
32
+ * the Registry tab simply shows no static emitters/listeners (no error).
33
+ */
34
+ async _loadStaticGraph() {
35
+ if (slice.events?.graph) return;
36
+ try {
37
+ const module = await import(/* @vite-ignore */ '/slice-events.generated.js');
38
+ if (module?.default) slice.events.loadGraph(module.default);
39
+ if (this.isOpen && this.activeTab === 'registry') this.renderRegistry();
40
+ } catch {
41
+ /* manifest not generated — documentation layer stays empty, runtime tracing still works */
42
+ }
43
+ }
44
+
28
45
  /**
29
46
  * Toggle panel visibility.
30
47
  * @returns {void}
@@ -69,11 +86,7 @@ export default class EventManagerDebugger extends HTMLElement {
69
86
  this._stopAutoRefresh();
70
87
  this._autoRefreshTimer = setInterval(() => {
71
88
  if (!this.isOpen) return;
72
- if (this.activeTab === 'history') {
73
- this.renderHistory();
74
- } else {
75
- this.renderList();
76
- }
89
+ this._renderActive();
77
90
  }, 1500);
78
91
  }
79
92
 
@@ -93,34 +106,36 @@ export default class EventManagerDebugger extends HTMLElement {
93
106
  this.refreshButton = this.querySelector('#events-refresh');
94
107
  this.closeButton = this.querySelector('#events-close');
95
108
  this.tabSubs = this.querySelector('#tab-subscribers');
109
+ this.tabRegistry = this.querySelector('#tab-registry');
96
110
  this.tabHistory = this.querySelector('#tab-history');
97
111
  }
98
112
 
99
113
  bindEvents() {
100
- this.refreshButton.addEventListener('click', () => {
101
- if (this.activeTab === 'history') this.renderHistory();
102
- else this.renderList();
103
- });
114
+ this.refreshButton.addEventListener('click', () => this._renderActive());
104
115
  this.closeButton.addEventListener('click', () => this.close());
105
116
  this.filterInput.addEventListener('input', (event) => {
106
117
  this.filterText = event.target.value.trim().toLowerCase();
107
- if (this.activeTab === 'history') this.renderHistory();
108
- else this.renderList();
109
- });
110
- this.tabSubs.addEventListener('click', () => {
111
- this.activeTab = 'subscribers';
112
- this.tabSubs.classList.add('active');
113
- this.tabHistory.classList.remove('active');
114
- this.filterInput.placeholder = 'filter events…';
115
- this.renderList();
116
- });
117
- this.tabHistory.addEventListener('click', () => {
118
- this.activeTab = 'history';
119
- this.tabHistory.classList.add('active');
120
- this.tabSubs.classList.remove('active');
121
- this.filterInput.placeholder = 'filter history…';
122
- this.renderHistory();
118
+ this._renderActive();
123
119
  });
120
+ this.tabSubs.addEventListener('click', () => this._selectTab('subscribers'));
121
+ this.tabRegistry.addEventListener('click', () => this._selectTab('registry'));
122
+ this.tabHistory.addEventListener('click', () => this._selectTab('history'));
123
+ }
124
+
125
+ _selectTab(tab) {
126
+ this.activeTab = tab;
127
+ this.tabSubs.classList.toggle('active', tab === 'subscribers');
128
+ this.tabRegistry.classList.toggle('active', tab === 'registry');
129
+ this.tabHistory.classList.toggle('active', tab === 'history');
130
+ const placeholders = { subscribers: 'filter events…', registry: 'filter registry…', history: 'filter history…' };
131
+ this.filterInput.placeholder = placeholders[tab] || 'filter…';
132
+ this._renderActive();
133
+ }
134
+
135
+ _renderActive() {
136
+ if (this.activeTab === 'history') this.renderHistory();
137
+ else if (this.activeTab === 'registry') this.renderRegistry();
138
+ else this.renderList();
124
139
  }
125
140
 
126
141
  makeDraggable() {
@@ -185,7 +200,7 @@ export default class EventManagerDebugger extends HTMLElement {
185
200
  return;
186
201
  }
187
202
 
188
- items.push({ eventName, count: subs.size, emitCount, entries });
203
+ items.push({ eventName, count: subs.size, emitCount, entries, emitters: this._emittersFor(eventName) });
189
204
  });
190
205
 
191
206
  items.sort((a, b) => a.eventName.localeCompare(b.eventName));
@@ -200,22 +215,36 @@ export default class EventManagerDebugger extends HTMLElement {
200
215
  const onceBadge = entry.once ? '<span class="badge">once</span>' : '';
201
216
  return `
202
217
  <div class="subscriber-row">
203
- <div class="subscriber-name">${label}</div>
218
+ <div class="subscriber-name">${this.escapeHtml(label)}</div>
204
219
  <div class="subscriber-meta">${entry.id}${onceBadge}</div>
205
220
  </div>
206
221
  `;
207
222
  }).join('');
208
223
 
224
+ const emittersHtml = item.emitters.length
225
+ ? item.emitters.map((emitter) => `
226
+ <div class="subscriber-row emitter-row">
227
+ <div class="subscriber-name">${this.escapeHtml(this._emitterLabel(emitter))}</div>
228
+ <div class="subscriber-meta">×${emitter.count}</div>
229
+ </div>
230
+ `).join('')
231
+ : '<div class="empty">No emitters recorded</div>';
232
+
209
233
  return `
210
234
  <details class="event-row">
211
235
  <summary>
212
- <div class="event-name">${item.eventName}</div>
236
+ <div class="event-name">${this.escapeHtml(item.eventName)}</div>
213
237
  <div class="event-metrics">
214
238
  <span class="emit-count" title="Emits this session">⚡${item.emitCount}</span>
215
239
  <span class="event-count">${item.count}</span>
216
240
  </div>
217
241
  </summary>
218
- <div class="subscriber-list">
242
+ <div class="trace-section">
243
+ <div class="trace-label">emitted by</div>
244
+ ${emittersHtml}
245
+ </div>
246
+ <div class="trace-section">
247
+ <div class="trace-label">listeners</div>
219
248
  ${details || '<div class="empty">No subscribers</div>'}
220
249
  </div>
221
250
  </details>
@@ -224,6 +253,173 @@ export default class EventManagerDebugger extends HTMLElement {
224
253
  : '<div class="empty">No events</div>';
225
254
  }
226
255
 
256
+ /**
257
+ * Emitters recorded for an event (only while the debugger is open).
258
+ * @param {string} eventName
259
+ * @returns {Array<{ sliceId?: string, name?: string, source?: string, count: number }>}
260
+ */
261
+ _emittersFor(eventName) {
262
+ const map = slice?.events?.emitters;
263
+ if (!(map instanceof Map) || !map.has(eventName)) return [];
264
+ return Array.from(map.get(eventName).values()).sort((a, b) => b.count - a.count);
265
+ }
266
+
267
+ /**
268
+ * Human-readable label for an emitter (component name + sliceId, or source location).
269
+ */
270
+ _emitterLabel(emitter) {
271
+ if (!emitter) return 'unknown';
272
+ if (emitter.sliceId) return `${emitter.name || 'Component'} (${emitter.sliceId})`;
273
+ if (emitter.source) return emitter.source;
274
+ return 'unknown';
275
+ }
276
+
277
+ /**
278
+ * Render the declared event catalog (registry) merged with the live state:
279
+ * payload shape, current subscriber count, emit count, plus drift badges for
280
+ * declared-but-unused and used-but-undeclared events.
281
+ */
282
+ renderRegistry() {
283
+ if (!slice?.events) {
284
+ this.list.textContent = 'EventManager not available.';
285
+ this.countLabel.textContent = '0';
286
+ return;
287
+ }
288
+
289
+ const registry = slice.events.registry instanceof Map ? slice.events.registry : new Map();
290
+ const undeclared = slice.events.undeclared instanceof Set ? slice.events.undeclared : new Set();
291
+
292
+ if (registry.size === 0 && undeclared.size === 0) {
293
+ this.countLabel.textContent = '0';
294
+ this.list.innerHTML =
295
+ '<div class="empty">No events registered.<br/>Declare them with <code>slice.events.register({ ... })</code>.</div>';
296
+ return;
297
+ }
298
+
299
+ const matchesFilter = (name) => !this.filterText || name.toLowerCase().includes(this.filterText);
300
+
301
+ // Bucket declared events by namespace (segment before the first ':').
302
+ const groups = new Map();
303
+ registry.forEach((definition, eventName) => {
304
+ if (!matchesFilter(eventName)) return;
305
+ const ns = slice.events.namespaceOf?.(eventName) || '(no namespace)';
306
+ if (!groups.has(ns)) groups.set(ns, []);
307
+ groups.get(ns).push({
308
+ eventName,
309
+ description: definition?.description || '',
310
+ payload: definition?.payload ?? null,
311
+ subscriberCount: slice.events.subscriberCount?.(eventName) || 0,
312
+ emitCount: slice.events.emitCounts?.get(eventName) || 0
313
+ });
314
+ });
315
+
316
+ const sortedNamespaces = Array.from(groups.keys()).sort((a, b) => a.localeCompare(b));
317
+ const declaredCount = Array.from(groups.values()).reduce((sum, list) => sum + list.length, 0);
318
+ const drift = Array.from(undeclared).filter(matchesFilter).sort();
319
+
320
+ this.countLabel.textContent = String(declaredCount + drift.length);
321
+
322
+ const siteRows = (sites) => sites.map((s) => {
323
+ const label = s.component ? `${s.component} — ${s.file}:${s.line}` : `${s.file}:${s.line}`;
324
+ return `<div class="graph-site">${this.escapeHtml(label)}</div>`;
325
+ }).join('');
326
+
327
+ const eventRowHtml = (item) => {
328
+ const payloadStr = this.formatPayload(item.payload);
329
+ const idle = item.subscriberCount === 0
330
+ ? '<span class="badge badge-idle" title="No active listeners">idle</span>'
331
+ : '';
332
+ const desc = item.description
333
+ ? `<div class="registry-desc">${this.escapeHtml(item.description)}</div>`
334
+ : '';
335
+
336
+ // Static documentation layer (from the generated manifest).
337
+ const emitters = slice.events.staticEmittersOf?.(item.eventName) || [];
338
+ const listeners = slice.events.staticListenersOf?.(item.eventName) || [];
339
+ const graphHtml = (emitters.length || listeners.length)
340
+ ? `
341
+ <div class="graph-block">
342
+ <div class="graph-label">emitters <span class="graph-tag">static</span></div>
343
+ ${emitters.length ? siteRows(emitters) : '<div class="empty">none found in code</div>'}
344
+ <div class="graph-label">listeners <span class="graph-tag">static</span></div>
345
+ ${listeners.length ? siteRows(listeners) : '<div class="empty">none found in code</div>'}
346
+ </div>`
347
+ : '';
348
+
349
+ return `
350
+ <details class="event-row">
351
+ <summary>
352
+ <div class="event-name">${this.escapeHtml(item.eventName)}</div>
353
+ <div class="event-metrics">
354
+ ${idle}
355
+ <span class="emit-count" title="Emits this session">⚡${item.emitCount}</span>
356
+ <span class="event-count" title="Active listeners">${item.subscriberCount}</span>
357
+ </div>
358
+ </summary>
359
+ <div class="subscriber-list">
360
+ ${desc}
361
+ <div class="registry-payload"><span class="payload-key">payload</span> ${this.escapeHtml(payloadStr)}</div>
362
+ ${graphHtml}
363
+ </div>
364
+ </details>
365
+ `;
366
+ };
367
+
368
+ const declaredHtml = sortedNamespaces.map((ns) => {
369
+ const list = groups.get(ns).sort((a, b) => a.eventName.localeCompare(b.eventName));
370
+ const rows = list.map(eventRowHtml).join('');
371
+ return `
372
+ <details class="ns-group" open>
373
+ <summary class="ns-summary">
374
+ <span class="ns-name">${this.escapeHtml(ns)}</span>
375
+ <span class="ns-count">${list.length}</span>
376
+ </summary>
377
+ <div class="ns-events">${rows}</div>
378
+ </details>
379
+ `;
380
+ }).join('');
381
+
382
+ const driftHtml = drift.length
383
+ ? `<div class="registry-drift-title">Used but not declared (${drift.length})</div>` +
384
+ drift.map((name) => `
385
+ <div class="history-row drift-row">
386
+ <div class="history-event">${this.escapeHtml(name)}</div>
387
+ <div class="history-time"><span class="badge badge-drift">undeclared</span></div>
388
+ </div>
389
+ `).join('')
390
+ : '';
391
+
392
+ // Footer: dynamic emit/subscribe sites (computed names the static scan can't resolve).
393
+ const dynamic = slice.events.graph?.dynamic;
394
+ const dynamicCount = (dynamic?.emitters?.length || 0) + (dynamic?.listeners?.length || 0);
395
+ const dynamicHtml = dynamicCount
396
+ ? `<div class="registry-drift-title">Dynamic sites (computed names, ${dynamicCount}) — not statically resolvable</div>`
397
+ : '';
398
+
399
+ this.list.innerHTML =
400
+ (declaredHtml || (drift.length ? '' : '<div class="empty">No matching events</div>')) + driftHtml + dynamicHtml;
401
+ }
402
+
403
+ /**
404
+ * Render a payload mini-schema for display.
405
+ * @param {object|null} payload
406
+ * @returns {string}
407
+ */
408
+ formatPayload(payload) {
409
+ if (payload === null || payload === undefined) return 'void';
410
+ if (typeof payload === 'string') return payload;
411
+ if (typeof payload === 'object') {
412
+ const parts = Object.entries(payload).map(([key, value]) => {
413
+ const type = typeof value === 'string'
414
+ ? value
415
+ : (value && typeof value === 'object' && value.type) || 'any';
416
+ return `${key}: ${type}`;
417
+ });
418
+ return `{ ${parts.join(', ')} }`;
419
+ }
420
+ return String(payload);
421
+ }
422
+
227
423
  renderHistory() {
228
424
  if (!slice?.events?.emitHistory) {
229
425
  this.list.textContent = 'EventManager not available.';
@@ -248,9 +444,15 @@ export default class EventManagerDebugger extends HTMLElement {
248
444
  const timeStr = diff < 1000 ? 'now'
249
445
  : diff < 60000 ? `${Math.floor(diff / 1000)}s ago`
250
446
  : `${Math.floor(diff / 60000)}m ago`;
447
+ const from = entry.emitter
448
+ ? `<div class="history-from" title="Emitter">↑ ${this.escapeHtml(this._emitterLabel(entry.emitter))}</div>`
449
+ : '';
251
450
  return `
252
451
  <div class="history-row">
253
- <div class="history-event">${this.escapeHtml(entry.eventName)}</div>
452
+ <div class="history-main">
453
+ <div class="history-event">${this.escapeHtml(entry.eventName)}</div>
454
+ ${from}
455
+ </div>
254
456
  <div class="history-time">${timeStr}</div>
255
457
  </div>
256
458
  `;
@@ -281,6 +483,7 @@ export default class EventManagerDebugger extends HTMLElement {
281
483
  </div>
282
484
  <div class="events-tabs">
283
485
  <button id="tab-subscribers" class="tab-btn active">Subscribers</button>
486
+ <button id="tab-registry" class="tab-btn">Registry</button>
284
487
  <button id="tab-history" class="tab-btn">History</button>
285
488
  </div>
286
489
  <div class="events-toolbar">
@@ -635,6 +838,141 @@ slice-eventmanager-debugger .empty {
635
838
  text-align: center;
636
839
  padding: 22px 0;
637
840
  }
841
+ slice-eventmanager-debugger .empty code {
842
+ font-family: var(--si-mono);
843
+ color: var(--si-accent);
844
+ background: rgba(var(--si-accent-rgb), 0.1);
845
+ padding: 1px 5px;
846
+ border-radius: 5px;
847
+ }
848
+
849
+ slice-eventmanager-debugger .registry-desc {
850
+ font-size: 11px;
851
+ color: var(--si-dim);
852
+ padding: 4px 2px 2px;
853
+ line-height: 1.4;
854
+ }
855
+ slice-eventmanager-debugger .registry-payload {
856
+ font-family: var(--si-mono);
857
+ font-size: 11px;
858
+ color: var(--si-text);
859
+ padding: 6px 9px;
860
+ border-radius: 7px;
861
+ background: rgba(0, 0, 0, 0.22);
862
+ border: 1px solid var(--si-border);
863
+ word-break: break-word;
864
+ }
865
+ slice-eventmanager-debugger .payload-key {
866
+ color: var(--si-dim);
867
+ margin-right: 6px;
868
+ }
869
+ slice-eventmanager-debugger .badge-idle {
870
+ background: rgba(136, 143, 166, 0.14);
871
+ color: var(--si-dim);
872
+ border: 1px solid var(--si-border);
873
+ }
874
+ slice-eventmanager-debugger .badge-drift {
875
+ background: rgba(255, 107, 107, 0.14);
876
+ color: var(--si-danger);
877
+ border: 1px solid rgba(255, 107, 107, 0.3);
878
+ }
879
+ slice-eventmanager-debugger .registry-drift-title {
880
+ margin-top: 4px;
881
+ font-size: 9.5px;
882
+ letter-spacing: 0.1em;
883
+ text-transform: uppercase;
884
+ color: var(--si-danger);
885
+ opacity: 0.85;
886
+ }
887
+ slice-eventmanager-debugger .drift-row { border-left-color: rgba(255, 107, 107, 0.4); }
888
+
889
+ slice-eventmanager-debugger .ns-group { display: block; }
890
+ slice-eventmanager-debugger .ns-group + .ns-group { margin-top: 4px; }
891
+ slice-eventmanager-debugger .ns-summary {
892
+ display: flex; align-items: center; gap: 8px; cursor: pointer; list-style: none;
893
+ padding: 4px 2px 6px; user-select: none;
894
+ }
895
+ slice-eventmanager-debugger .ns-summary::-webkit-details-marker { display: none; }
896
+ slice-eventmanager-debugger .ns-summary::before {
897
+ content: '▾'; color: var(--si-dim); font-size: 10px; transition: transform 0.2s ease;
898
+ }
899
+ slice-eventmanager-debugger .ns-group:not([open]) .ns-summary::before { transform: rotate(-90deg); }
900
+ slice-eventmanager-debugger .ns-name {
901
+ font-size: 9.5px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--si-accent);
902
+ }
903
+ slice-eventmanager-debugger .ns-count {
904
+ font-size: 9.5px; color: var(--si-dim);
905
+ background: var(--si-raised-2); border: 1px solid var(--si-border);
906
+ border-radius: 999px; padding: 0 6px;
907
+ }
908
+ slice-eventmanager-debugger .ns-events { display: flex; flex-direction: column; gap: 7px; padding-left: 4px; }
909
+
910
+ slice-eventmanager-debugger .trace-section {
911
+ margin-top: 9px;
912
+ padding-top: 9px;
913
+ border-top: 1px dashed var(--si-border);
914
+ display: flex;
915
+ flex-direction: column;
916
+ gap: 6px;
917
+ }
918
+ slice-eventmanager-debugger .trace-label {
919
+ font-size: 9px;
920
+ font-weight: 700;
921
+ letter-spacing: 0.1em;
922
+ text-transform: uppercase;
923
+ color: var(--si-dim);
924
+ }
925
+ slice-eventmanager-debugger .emitter-row { border-left: 2px solid var(--si-success); }
926
+ slice-eventmanager-debugger .emitter-row .subscriber-name { color: var(--si-text); }
927
+
928
+ slice-eventmanager-debugger .history-main { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
929
+ slice-eventmanager-debugger .history-from {
930
+ font-size: 10px;
931
+ color: var(--si-success);
932
+ overflow: hidden;
933
+ text-overflow: ellipsis;
934
+ white-space: nowrap;
935
+ }
936
+
937
+ slice-eventmanager-debugger .graph-block {
938
+ margin-top: 8px;
939
+ padding-top: 8px;
940
+ border-top: 1px dashed var(--si-border);
941
+ display: flex;
942
+ flex-direction: column;
943
+ gap: 4px;
944
+ }
945
+ slice-eventmanager-debugger .graph-label {
946
+ font-size: 9px;
947
+ font-weight: 700;
948
+ letter-spacing: 0.08em;
949
+ text-transform: uppercase;
950
+ color: var(--si-dim);
951
+ margin-top: 4px;
952
+ display: flex;
953
+ align-items: center;
954
+ gap: 6px;
955
+ }
956
+ slice-eventmanager-debugger .graph-tag {
957
+ font-size: 8px;
958
+ font-weight: 600;
959
+ letter-spacing: 0.04em;
960
+ color: var(--si-accent);
961
+ background: rgba(var(--si-accent-rgb), 0.12);
962
+ border: 1px solid rgba(var(--si-accent-rgb), 0.25);
963
+ border-radius: 999px;
964
+ padding: 0 5px;
965
+ }
966
+ slice-eventmanager-debugger .graph-site {
967
+ font-family: var(--si-mono);
968
+ font-size: 10.5px;
969
+ color: var(--si-text);
970
+ padding: 3px 8px;
971
+ border-radius: 6px;
972
+ background: rgba(0, 0, 0, 0.2);
973
+ border: 1px solid var(--si-border);
974
+ word-break: break-word;
975
+ }
638
976
 
639
977
  @media (prefers-reduced-motion: reduce) {
640
978
  slice-eventmanager-debugger #events-debugger.active { animation: none; }