@ngstato/angular 0.1.3 → 0.1.4
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.
- package/README.md +6 -0
- package/dist/README.md +6 -0
- package/dist/devtools.component.d.ts +0 -1
- package/dist/esm2022/devtools.component.mjs +248 -250
- package/dist/fesm2022/ngstato-angular.mjs +246 -247
- package/dist/fesm2022/ngstato-angular.mjs.map +1 -1
- package/ng-package.json +2 -1
- package/package.json +14 -1
|
@@ -162,7 +162,6 @@ function StatoStore(config) {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
class StatoDevToolsComponent {
|
|
165
|
-
config = inject(STATO_CONFIG, { optional: true });
|
|
166
165
|
unsub;
|
|
167
166
|
// State UI
|
|
168
167
|
isOpen = signal(false);
|
|
@@ -248,256 +247,256 @@ class StatoDevToolsComponent {
|
|
|
248
247
|
this.isResizing = false;
|
|
249
248
|
}
|
|
250
249
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StatoDevToolsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
251
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: StatoDevToolsComponent, isStandalone: true, selector: "ngstato-devtools", ngImport: i0, template: `
|
|
252
|
-
<!-- Bouton flottant -->
|
|
253
|
-
@if (!isOpen()) {
|
|
254
|
-
<button class="devtools-fab" (click)="toggle()">
|
|
255
|
-
🛠 Stato
|
|
256
|
-
</button>
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
<!-- Panel -->
|
|
260
|
-
@if (isOpen()) {
|
|
261
|
-
<div
|
|
262
|
-
class="devtools-panel"
|
|
263
|
-
[class.devtools-panel--minimized]="isMinimized()"
|
|
264
|
-
[style.left.px]="posX()"
|
|
265
|
-
[style.top.px]="posY()"
|
|
266
|
-
[style.width.px]="isMinimized() ? 200 : panelWidth()"
|
|
267
|
-
[style.height]="isMinimized() ? 'auto' : panelHeight() + 'px'"
|
|
268
|
-
>
|
|
269
|
-
|
|
270
|
-
<!-- Header — draggable -->
|
|
271
|
-
<div
|
|
272
|
-
class="devtools-header"
|
|
273
|
-
(mousedown)="onDragStart($event)"
|
|
274
|
-
>
|
|
275
|
-
<span class="devtools-title">🛠 Stato</span>
|
|
276
|
-
<div class="devtools-header-actions">
|
|
277
|
-
@if (!isMinimized()) {
|
|
278
|
-
<button class="btn-icon" (click)="clear()" title="Vider">🗑</button>
|
|
279
|
-
}
|
|
280
|
-
<button class="btn-icon" (click)="toggleMinimize()" title="Minimiser/Agrandir">
|
|
281
|
-
{{ isMinimized() ? '▲' : '▼' }}
|
|
282
|
-
</button>
|
|
283
|
-
<button class="btn-icon" (click)="toggle()" title="Fermer">✕</button>
|
|
284
|
-
</div>
|
|
285
|
-
</div>
|
|
286
|
-
|
|
287
|
-
<!-- Resize handle — coin bas droite -->
|
|
288
|
-
@if (!isMinimized()) {
|
|
289
|
-
<div
|
|
290
|
-
class="devtools-resize"
|
|
291
|
-
(mousedown)="onResizeStart($event)"
|
|
292
|
-
>⊿</div>
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
@if (!isMinimized()) {
|
|
296
|
-
|
|
297
|
-
<!-- Tabs -->
|
|
298
|
-
<div class="devtools-tabs">
|
|
299
|
-
<button
|
|
300
|
-
class="tab"
|
|
301
|
-
[class.tab--active]="activeTab() === 'actions'"
|
|
302
|
-
(click)="activeTab.set('actions')"
|
|
303
|
-
>
|
|
304
|
-
Actions ({{ logs().length }})
|
|
305
|
-
</button>
|
|
306
|
-
<button
|
|
307
|
-
class="tab"
|
|
308
|
-
[class.tab--active]="activeTab() === 'state'"
|
|
309
|
-
(click)="activeTab.set('state')"
|
|
310
|
-
>
|
|
311
|
-
State
|
|
312
|
-
</button>
|
|
313
|
-
</div>
|
|
314
|
-
|
|
315
|
-
<!-- Tab Actions -->
|
|
316
|
-
@if (activeTab() === 'actions') {
|
|
317
|
-
<div class="devtools-content">
|
|
318
|
-
@if (!logs().length) {
|
|
319
|
-
<div class="devtools-empty">Aucune action pour l'instant</div>
|
|
320
|
-
}
|
|
321
|
-
@for (log of logs(); track log.id) {
|
|
322
|
-
<div
|
|
323
|
-
class="log-item"
|
|
324
|
-
[class.log-item--error]="log.status === 'error'"
|
|
325
|
-
(click)="selectLog(log)"
|
|
326
|
-
>
|
|
327
|
-
<div class="log-item__left">
|
|
328
|
-
<span class="log-status">{{ log.status === 'success' ? '✓' : '✗' }}</span>
|
|
329
|
-
<span class="log-name">{{ log.name }}</span>
|
|
330
|
-
</div>
|
|
331
|
-
<div class="log-item__right">
|
|
332
|
-
@if (log.status === 'error') {
|
|
333
|
-
<span class="log-error-badge">erreur</span>
|
|
334
|
-
} @else {
|
|
335
|
-
<span class="log-duration">{{ log.duration }}ms</span>
|
|
336
|
-
}
|
|
337
|
-
<span class="log-time">{{ formatTime(log.at) }}</span>
|
|
338
|
-
</div>
|
|
339
|
-
</div>
|
|
340
|
-
|
|
341
|
-
@if (selectedLog()?.id === log.id) {
|
|
342
|
-
<div class="log-detail">
|
|
343
|
-
@if (log.error) {
|
|
344
|
-
<div class="log-detail__error">{{ log.error }}</div>
|
|
345
|
-
}
|
|
346
|
-
<div class="log-detail__section">
|
|
347
|
-
<span class="log-detail__label">Avant</span>
|
|
348
|
-
<pre>{{ log.prevState | json }}</pre>
|
|
349
|
-
</div>
|
|
350
|
-
<div class="log-detail__section">
|
|
351
|
-
<span class="log-detail__label">Après</span>
|
|
352
|
-
<pre>{{ log.nextState | json }}</pre>
|
|
353
|
-
</div>
|
|
354
|
-
</div>
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
</div>
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
<!-- Tab State -->
|
|
361
|
-
@if (activeTab() === 'state') {
|
|
362
|
-
<div class="devtools-content">
|
|
363
|
-
@if (logs().length) {
|
|
364
|
-
<pre class="state-view">{{ logs()[0].nextState | json }}</pre>
|
|
365
|
-
} @else {
|
|
366
|
-
<div class="devtools-empty">Aucun state disponible</div>
|
|
367
|
-
}
|
|
368
|
-
</div>
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
</div>
|
|
373
|
-
}
|
|
250
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: StatoDevToolsComponent, isStandalone: true, selector: "ngstato-devtools", ngImport: i0, template: `
|
|
251
|
+
<!-- Bouton flottant -->
|
|
252
|
+
@if (!isOpen()) {
|
|
253
|
+
<button class="devtools-fab" (click)="toggle()">
|
|
254
|
+
🛠 Stato
|
|
255
|
+
</button>
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
<!-- Panel -->
|
|
259
|
+
@if (isOpen()) {
|
|
260
|
+
<div
|
|
261
|
+
class="devtools-panel"
|
|
262
|
+
[class.devtools-panel--minimized]="isMinimized()"
|
|
263
|
+
[style.left.px]="posX()"
|
|
264
|
+
[style.top.px]="posY()"
|
|
265
|
+
[style.width.px]="isMinimized() ? 200 : panelWidth()"
|
|
266
|
+
[style.height]="isMinimized() ? 'auto' : panelHeight() + 'px'"
|
|
267
|
+
>
|
|
268
|
+
|
|
269
|
+
<!-- Header — draggable -->
|
|
270
|
+
<div
|
|
271
|
+
class="devtools-header"
|
|
272
|
+
(mousedown)="onDragStart($event)"
|
|
273
|
+
>
|
|
274
|
+
<span class="devtools-title">🛠 Stato</span>
|
|
275
|
+
<div class="devtools-header-actions">
|
|
276
|
+
@if (!isMinimized()) {
|
|
277
|
+
<button class="btn-icon" (click)="clear()" title="Vider">🗑</button>
|
|
278
|
+
}
|
|
279
|
+
<button class="btn-icon" (click)="toggleMinimize()" title="Minimiser/Agrandir">
|
|
280
|
+
{{ isMinimized() ? '▲' : '▼' }}
|
|
281
|
+
</button>
|
|
282
|
+
<button class="btn-icon" (click)="toggle()" title="Fermer">✕</button>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
|
|
286
|
+
<!-- Resize handle — coin bas droite -->
|
|
287
|
+
@if (!isMinimized()) {
|
|
288
|
+
<div
|
|
289
|
+
class="devtools-resize"
|
|
290
|
+
(mousedown)="onResizeStart($event)"
|
|
291
|
+
>⊿</div>
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
@if (!isMinimized()) {
|
|
295
|
+
|
|
296
|
+
<!-- Tabs -->
|
|
297
|
+
<div class="devtools-tabs">
|
|
298
|
+
<button
|
|
299
|
+
class="tab"
|
|
300
|
+
[class.tab--active]="activeTab() === 'actions'"
|
|
301
|
+
(click)="activeTab.set('actions')"
|
|
302
|
+
>
|
|
303
|
+
Actions ({{ logs().length }})
|
|
304
|
+
</button>
|
|
305
|
+
<button
|
|
306
|
+
class="tab"
|
|
307
|
+
[class.tab--active]="activeTab() === 'state'"
|
|
308
|
+
(click)="activeTab.set('state')"
|
|
309
|
+
>
|
|
310
|
+
State
|
|
311
|
+
</button>
|
|
312
|
+
</div>
|
|
313
|
+
|
|
314
|
+
<!-- Tab Actions -->
|
|
315
|
+
@if (activeTab() === 'actions') {
|
|
316
|
+
<div class="devtools-content">
|
|
317
|
+
@if (!logs().length) {
|
|
318
|
+
<div class="devtools-empty">Aucune action pour l'instant</div>
|
|
319
|
+
}
|
|
320
|
+
@for (log of logs(); track log.id) {
|
|
321
|
+
<div
|
|
322
|
+
class="log-item"
|
|
323
|
+
[class.log-item--error]="log.status === 'error'"
|
|
324
|
+
(click)="selectLog(log)"
|
|
325
|
+
>
|
|
326
|
+
<div class="log-item__left">
|
|
327
|
+
<span class="log-status">{{ log.status === 'success' ? '✓' : '✗' }}</span>
|
|
328
|
+
<span class="log-name">{{ log.name }}</span>
|
|
329
|
+
</div>
|
|
330
|
+
<div class="log-item__right">
|
|
331
|
+
@if (log.status === 'error') {
|
|
332
|
+
<span class="log-error-badge">erreur</span>
|
|
333
|
+
} @else {
|
|
334
|
+
<span class="log-duration">{{ log.duration }}ms</span>
|
|
335
|
+
}
|
|
336
|
+
<span class="log-time">{{ formatTime(log.at) }}</span>
|
|
337
|
+
</div>
|
|
338
|
+
</div>
|
|
339
|
+
|
|
340
|
+
@if (selectedLog()?.id === log.id) {
|
|
341
|
+
<div class="log-detail">
|
|
342
|
+
@if (log.error) {
|
|
343
|
+
<div class="log-detail__error">{{ log.error }}</div>
|
|
344
|
+
}
|
|
345
|
+
<div class="log-detail__section">
|
|
346
|
+
<span class="log-detail__label">Avant</span>
|
|
347
|
+
<pre>{{ log.prevState | json }}</pre>
|
|
348
|
+
</div>
|
|
349
|
+
<div class="log-detail__section">
|
|
350
|
+
<span class="log-detail__label">Après</span>
|
|
351
|
+
<pre>{{ log.nextState | json }}</pre>
|
|
352
|
+
</div>
|
|
353
|
+
</div>
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
</div>
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
<!-- Tab State -->
|
|
360
|
+
@if (activeTab() === 'state') {
|
|
361
|
+
<div class="devtools-content">
|
|
362
|
+
@if (logs().length) {
|
|
363
|
+
<pre class="state-view">{{ logs()[0].nextState | json }}</pre>
|
|
364
|
+
} @else {
|
|
365
|
+
<div class="devtools-empty">Aucun state disponible</div>
|
|
366
|
+
}
|
|
367
|
+
</div>
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
</div>
|
|
372
|
+
}
|
|
374
373
|
`, isInline: true, styles: [".devtools-fab{position:fixed;bottom:1.5rem;left:1.5rem;background:#1e293b;color:#fff;border:none;border-radius:999px;padding:.5rem 1rem;font-size:.85rem;font-weight:600;cursor:pointer;z-index:9999;box-shadow:0 4px 12px #0000004d;transition:background .15s}.devtools-fab:hover{background:#334155}.devtools-panel{position:fixed;background:#0f172a;border-radius:12px;box-shadow:0 8px 32px #0006;z-index:9999;display:flex;flex-direction:column;overflow:hidden;font-family:Courier New,monospace;min-width:200px;min-height:40px}.devtools-panel--minimized{border-radius:8px}.devtools-header{display:flex;justify-content:space-between;align-items:center;padding:.6rem .75rem;background:#1e293b;border-bottom:1px solid #334155;cursor:grab;-webkit-user-select:none;user-select:none}.devtools-header:active{cursor:grabbing}.devtools-title{color:#e2e8f0;font-size:.82rem;font-weight:600;font-family:system-ui}.devtools-header-actions{display:flex;gap:.25rem}.btn-icon{background:transparent;color:#64748b;border:none;cursor:pointer;font-size:.8rem;padding:.15rem .35rem;border-radius:4px;line-height:1}.btn-icon:hover{background:#334155;color:#fff}.devtools-resize{position:absolute;bottom:2px;right:4px;color:#334155;font-size:.9rem;cursor:nwse-resize;-webkit-user-select:none;user-select:none;line-height:1}.devtools-resize:hover{color:#64748b}.devtools-tabs{display:flex;background:#1e293b;border-bottom:1px solid #334155}.tab{padding:.4rem .75rem;background:transparent;color:#64748b;border:none;cursor:pointer;font-size:.78rem;font-family:system-ui}.tab:hover{color:#e2e8f0}.tab--active{color:#3b82f6;border-bottom:2px solid #3b82f6}.devtools-content{overflow-y:auto;flex:1;padding:.25rem 0}.devtools-empty{padding:2rem;text-align:center;color:#475569;font-size:.78rem;font-family:system-ui}.log-item{display:flex;justify-content:space-between;align-items:center;padding:.35rem .75rem;cursor:pointer;border-bottom:1px solid #1e293b}.log-item:hover{background:#1e293b}.log-item--error{background:#1a0a0a}.log-item__left{display:flex;align-items:center;gap:.4rem;overflow:hidden}.log-item__right{display:flex;align-items:center;gap:.4rem;flex-shrink:0}.log-status{font-size:.72rem;color:#22c55e;flex-shrink:0}.log-item--error .log-status{color:#ef4444}.log-name{color:#e2e8f0;font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.log-duration{color:#64748b;font-size:.7rem}.log-time{color:#475569;font-size:.68rem}.log-error-badge{background:#7f1d1d;color:#fca5a5;font-size:.68rem;padding:.1rem .35rem;border-radius:4px}.log-detail{background:#0a0f1a;padding:.6rem .75rem;border-left:3px solid #3b82f6;margin:0 .4rem .4rem;border-radius:0 4px 4px 0}.log-detail__error{color:#fca5a5;font-size:.72rem;margin-bottom:.4rem}.log-detail__section{margin-bottom:.4rem}.log-detail__label{color:#64748b;font-size:.68rem;display:block;margin-bottom:.2rem;font-family:system-ui}pre{color:#86efac;font-size:.7rem;margin:0;white-space:pre-wrap;word-break:break-all;max-height:140px;overflow-y:auto}.state-view{color:#86efac;font-size:.7rem;padding:.75rem;margin:0;white-space:pre-wrap;word-break:break-all}\n"], dependencies: [{ kind: "pipe", type: JsonPipe, name: "json" }] });
|
|
375
374
|
}
|
|
376
375
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: StatoDevToolsComponent, decorators: [{
|
|
377
376
|
type: Component,
|
|
378
|
-
args: [{ selector: 'ngstato-devtools', standalone: true, imports: [JsonPipe], template: `
|
|
379
|
-
<!-- Bouton flottant -->
|
|
380
|
-
@if (!isOpen()) {
|
|
381
|
-
<button class="devtools-fab" (click)="toggle()">
|
|
382
|
-
🛠 Stato
|
|
383
|
-
</button>
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
<!-- Panel -->
|
|
387
|
-
@if (isOpen()) {
|
|
388
|
-
<div
|
|
389
|
-
class="devtools-panel"
|
|
390
|
-
[class.devtools-panel--minimized]="isMinimized()"
|
|
391
|
-
[style.left.px]="posX()"
|
|
392
|
-
[style.top.px]="posY()"
|
|
393
|
-
[style.width.px]="isMinimized() ? 200 : panelWidth()"
|
|
394
|
-
[style.height]="isMinimized() ? 'auto' : panelHeight() + 'px'"
|
|
395
|
-
>
|
|
396
|
-
|
|
397
|
-
<!-- Header — draggable -->
|
|
398
|
-
<div
|
|
399
|
-
class="devtools-header"
|
|
400
|
-
(mousedown)="onDragStart($event)"
|
|
401
|
-
>
|
|
402
|
-
<span class="devtools-title">🛠 Stato</span>
|
|
403
|
-
<div class="devtools-header-actions">
|
|
404
|
-
@if (!isMinimized()) {
|
|
405
|
-
<button class="btn-icon" (click)="clear()" title="Vider">🗑</button>
|
|
406
|
-
}
|
|
407
|
-
<button class="btn-icon" (click)="toggleMinimize()" title="Minimiser/Agrandir">
|
|
408
|
-
{{ isMinimized() ? '▲' : '▼' }}
|
|
409
|
-
</button>
|
|
410
|
-
<button class="btn-icon" (click)="toggle()" title="Fermer">✕</button>
|
|
411
|
-
</div>
|
|
412
|
-
</div>
|
|
413
|
-
|
|
414
|
-
<!-- Resize handle — coin bas droite -->
|
|
415
|
-
@if (!isMinimized()) {
|
|
416
|
-
<div
|
|
417
|
-
class="devtools-resize"
|
|
418
|
-
(mousedown)="onResizeStart($event)"
|
|
419
|
-
>⊿</div>
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
@if (!isMinimized()) {
|
|
423
|
-
|
|
424
|
-
<!-- Tabs -->
|
|
425
|
-
<div class="devtools-tabs">
|
|
426
|
-
<button
|
|
427
|
-
class="tab"
|
|
428
|
-
[class.tab--active]="activeTab() === 'actions'"
|
|
429
|
-
(click)="activeTab.set('actions')"
|
|
430
|
-
>
|
|
431
|
-
Actions ({{ logs().length }})
|
|
432
|
-
</button>
|
|
433
|
-
<button
|
|
434
|
-
class="tab"
|
|
435
|
-
[class.tab--active]="activeTab() === 'state'"
|
|
436
|
-
(click)="activeTab.set('state')"
|
|
437
|
-
>
|
|
438
|
-
State
|
|
439
|
-
</button>
|
|
440
|
-
</div>
|
|
441
|
-
|
|
442
|
-
<!-- Tab Actions -->
|
|
443
|
-
@if (activeTab() === 'actions') {
|
|
444
|
-
<div class="devtools-content">
|
|
445
|
-
@if (!logs().length) {
|
|
446
|
-
<div class="devtools-empty">Aucune action pour l'instant</div>
|
|
447
|
-
}
|
|
448
|
-
@for (log of logs(); track log.id) {
|
|
449
|
-
<div
|
|
450
|
-
class="log-item"
|
|
451
|
-
[class.log-item--error]="log.status === 'error'"
|
|
452
|
-
(click)="selectLog(log)"
|
|
453
|
-
>
|
|
454
|
-
<div class="log-item__left">
|
|
455
|
-
<span class="log-status">{{ log.status === 'success' ? '✓' : '✗' }}</span>
|
|
456
|
-
<span class="log-name">{{ log.name }}</span>
|
|
457
|
-
</div>
|
|
458
|
-
<div class="log-item__right">
|
|
459
|
-
@if (log.status === 'error') {
|
|
460
|
-
<span class="log-error-badge">erreur</span>
|
|
461
|
-
} @else {
|
|
462
|
-
<span class="log-duration">{{ log.duration }}ms</span>
|
|
463
|
-
}
|
|
464
|
-
<span class="log-time">{{ formatTime(log.at) }}</span>
|
|
465
|
-
</div>
|
|
466
|
-
</div>
|
|
467
|
-
|
|
468
|
-
@if (selectedLog()?.id === log.id) {
|
|
469
|
-
<div class="log-detail">
|
|
470
|
-
@if (log.error) {
|
|
471
|
-
<div class="log-detail__error">{{ log.error }}</div>
|
|
472
|
-
}
|
|
473
|
-
<div class="log-detail__section">
|
|
474
|
-
<span class="log-detail__label">Avant</span>
|
|
475
|
-
<pre>{{ log.prevState | json }}</pre>
|
|
476
|
-
</div>
|
|
477
|
-
<div class="log-detail__section">
|
|
478
|
-
<span class="log-detail__label">Après</span>
|
|
479
|
-
<pre>{{ log.nextState | json }}</pre>
|
|
480
|
-
</div>
|
|
481
|
-
</div>
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
</div>
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
<!-- Tab State -->
|
|
488
|
-
@if (activeTab() === 'state') {
|
|
489
|
-
<div class="devtools-content">
|
|
490
|
-
@if (logs().length) {
|
|
491
|
-
<pre class="state-view">{{ logs()[0].nextState | json }}</pre>
|
|
492
|
-
} @else {
|
|
493
|
-
<div class="devtools-empty">Aucun state disponible</div>
|
|
494
|
-
}
|
|
495
|
-
</div>
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
</div>
|
|
500
|
-
}
|
|
377
|
+
args: [{ selector: 'ngstato-devtools', standalone: true, imports: [JsonPipe], template: `
|
|
378
|
+
<!-- Bouton flottant -->
|
|
379
|
+
@if (!isOpen()) {
|
|
380
|
+
<button class="devtools-fab" (click)="toggle()">
|
|
381
|
+
🛠 Stato
|
|
382
|
+
</button>
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
<!-- Panel -->
|
|
386
|
+
@if (isOpen()) {
|
|
387
|
+
<div
|
|
388
|
+
class="devtools-panel"
|
|
389
|
+
[class.devtools-panel--minimized]="isMinimized()"
|
|
390
|
+
[style.left.px]="posX()"
|
|
391
|
+
[style.top.px]="posY()"
|
|
392
|
+
[style.width.px]="isMinimized() ? 200 : panelWidth()"
|
|
393
|
+
[style.height]="isMinimized() ? 'auto' : panelHeight() + 'px'"
|
|
394
|
+
>
|
|
395
|
+
|
|
396
|
+
<!-- Header — draggable -->
|
|
397
|
+
<div
|
|
398
|
+
class="devtools-header"
|
|
399
|
+
(mousedown)="onDragStart($event)"
|
|
400
|
+
>
|
|
401
|
+
<span class="devtools-title">🛠 Stato</span>
|
|
402
|
+
<div class="devtools-header-actions">
|
|
403
|
+
@if (!isMinimized()) {
|
|
404
|
+
<button class="btn-icon" (click)="clear()" title="Vider">🗑</button>
|
|
405
|
+
}
|
|
406
|
+
<button class="btn-icon" (click)="toggleMinimize()" title="Minimiser/Agrandir">
|
|
407
|
+
{{ isMinimized() ? '▲' : '▼' }}
|
|
408
|
+
</button>
|
|
409
|
+
<button class="btn-icon" (click)="toggle()" title="Fermer">✕</button>
|
|
410
|
+
</div>
|
|
411
|
+
</div>
|
|
412
|
+
|
|
413
|
+
<!-- Resize handle — coin bas droite -->
|
|
414
|
+
@if (!isMinimized()) {
|
|
415
|
+
<div
|
|
416
|
+
class="devtools-resize"
|
|
417
|
+
(mousedown)="onResizeStart($event)"
|
|
418
|
+
>⊿</div>
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
@if (!isMinimized()) {
|
|
422
|
+
|
|
423
|
+
<!-- Tabs -->
|
|
424
|
+
<div class="devtools-tabs">
|
|
425
|
+
<button
|
|
426
|
+
class="tab"
|
|
427
|
+
[class.tab--active]="activeTab() === 'actions'"
|
|
428
|
+
(click)="activeTab.set('actions')"
|
|
429
|
+
>
|
|
430
|
+
Actions ({{ logs().length }})
|
|
431
|
+
</button>
|
|
432
|
+
<button
|
|
433
|
+
class="tab"
|
|
434
|
+
[class.tab--active]="activeTab() === 'state'"
|
|
435
|
+
(click)="activeTab.set('state')"
|
|
436
|
+
>
|
|
437
|
+
State
|
|
438
|
+
</button>
|
|
439
|
+
</div>
|
|
440
|
+
|
|
441
|
+
<!-- Tab Actions -->
|
|
442
|
+
@if (activeTab() === 'actions') {
|
|
443
|
+
<div class="devtools-content">
|
|
444
|
+
@if (!logs().length) {
|
|
445
|
+
<div class="devtools-empty">Aucune action pour l'instant</div>
|
|
446
|
+
}
|
|
447
|
+
@for (log of logs(); track log.id) {
|
|
448
|
+
<div
|
|
449
|
+
class="log-item"
|
|
450
|
+
[class.log-item--error]="log.status === 'error'"
|
|
451
|
+
(click)="selectLog(log)"
|
|
452
|
+
>
|
|
453
|
+
<div class="log-item__left">
|
|
454
|
+
<span class="log-status">{{ log.status === 'success' ? '✓' : '✗' }}</span>
|
|
455
|
+
<span class="log-name">{{ log.name }}</span>
|
|
456
|
+
</div>
|
|
457
|
+
<div class="log-item__right">
|
|
458
|
+
@if (log.status === 'error') {
|
|
459
|
+
<span class="log-error-badge">erreur</span>
|
|
460
|
+
} @else {
|
|
461
|
+
<span class="log-duration">{{ log.duration }}ms</span>
|
|
462
|
+
}
|
|
463
|
+
<span class="log-time">{{ formatTime(log.at) }}</span>
|
|
464
|
+
</div>
|
|
465
|
+
</div>
|
|
466
|
+
|
|
467
|
+
@if (selectedLog()?.id === log.id) {
|
|
468
|
+
<div class="log-detail">
|
|
469
|
+
@if (log.error) {
|
|
470
|
+
<div class="log-detail__error">{{ log.error }}</div>
|
|
471
|
+
}
|
|
472
|
+
<div class="log-detail__section">
|
|
473
|
+
<span class="log-detail__label">Avant</span>
|
|
474
|
+
<pre>{{ log.prevState | json }}</pre>
|
|
475
|
+
</div>
|
|
476
|
+
<div class="log-detail__section">
|
|
477
|
+
<span class="log-detail__label">Après</span>
|
|
478
|
+
<pre>{{ log.nextState | json }}</pre>
|
|
479
|
+
</div>
|
|
480
|
+
</div>
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
</div>
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
<!-- Tab State -->
|
|
487
|
+
@if (activeTab() === 'state') {
|
|
488
|
+
<div class="devtools-content">
|
|
489
|
+
@if (logs().length) {
|
|
490
|
+
<pre class="state-view">{{ logs()[0].nextState | json }}</pre>
|
|
491
|
+
} @else {
|
|
492
|
+
<div class="devtools-empty">Aucun state disponible</div>
|
|
493
|
+
}
|
|
494
|
+
</div>
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
</div>
|
|
499
|
+
}
|
|
501
500
|
`, styles: [".devtools-fab{position:fixed;bottom:1.5rem;left:1.5rem;background:#1e293b;color:#fff;border:none;border-radius:999px;padding:.5rem 1rem;font-size:.85rem;font-weight:600;cursor:pointer;z-index:9999;box-shadow:0 4px 12px #0000004d;transition:background .15s}.devtools-fab:hover{background:#334155}.devtools-panel{position:fixed;background:#0f172a;border-radius:12px;box-shadow:0 8px 32px #0006;z-index:9999;display:flex;flex-direction:column;overflow:hidden;font-family:Courier New,monospace;min-width:200px;min-height:40px}.devtools-panel--minimized{border-radius:8px}.devtools-header{display:flex;justify-content:space-between;align-items:center;padding:.6rem .75rem;background:#1e293b;border-bottom:1px solid #334155;cursor:grab;-webkit-user-select:none;user-select:none}.devtools-header:active{cursor:grabbing}.devtools-title{color:#e2e8f0;font-size:.82rem;font-weight:600;font-family:system-ui}.devtools-header-actions{display:flex;gap:.25rem}.btn-icon{background:transparent;color:#64748b;border:none;cursor:pointer;font-size:.8rem;padding:.15rem .35rem;border-radius:4px;line-height:1}.btn-icon:hover{background:#334155;color:#fff}.devtools-resize{position:absolute;bottom:2px;right:4px;color:#334155;font-size:.9rem;cursor:nwse-resize;-webkit-user-select:none;user-select:none;line-height:1}.devtools-resize:hover{color:#64748b}.devtools-tabs{display:flex;background:#1e293b;border-bottom:1px solid #334155}.tab{padding:.4rem .75rem;background:transparent;color:#64748b;border:none;cursor:pointer;font-size:.78rem;font-family:system-ui}.tab:hover{color:#e2e8f0}.tab--active{color:#3b82f6;border-bottom:2px solid #3b82f6}.devtools-content{overflow-y:auto;flex:1;padding:.25rem 0}.devtools-empty{padding:2rem;text-align:center;color:#475569;font-size:.78rem;font-family:system-ui}.log-item{display:flex;justify-content:space-between;align-items:center;padding:.35rem .75rem;cursor:pointer;border-bottom:1px solid #1e293b}.log-item:hover{background:#1e293b}.log-item--error{background:#1a0a0a}.log-item__left{display:flex;align-items:center;gap:.4rem;overflow:hidden}.log-item__right{display:flex;align-items:center;gap:.4rem;flex-shrink:0}.log-status{font-size:.72rem;color:#22c55e;flex-shrink:0}.log-item--error .log-status{color:#ef4444}.log-name{color:#e2e8f0;font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.log-duration{color:#64748b;font-size:.7rem}.log-time{color:#475569;font-size:.68rem}.log-error-badge{background:#7f1d1d;color:#fca5a5;font-size:.68rem;padding:.1rem .35rem;border-radius:4px}.log-detail{background:#0a0f1a;padding:.6rem .75rem;border-left:3px solid #3b82f6;margin:0 .4rem .4rem;border-radius:0 4px 4px 0}.log-detail__error{color:#fca5a5;font-size:.72rem;margin-bottom:.4rem}.log-detail__section{margin-bottom:.4rem}.log-detail__label{color:#64748b;font-size:.68rem;display:block;margin-bottom:.2rem;font-family:system-ui}pre{color:#86efac;font-size:.7rem;margin:0;white-space:pre-wrap;word-break:break-all;max-height:140px;overflow-y:auto}.state-view{color:#86efac;font-size:.7rem;padding:.75rem;margin:0;white-space:pre-wrap;word-break:break-all}\n"] }]
|
|
502
501
|
}] });
|
|
503
502
|
|