@ai-matrx/canvas 0.4.0 → 0.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.
- package/CHANGELOG.md +40 -0
- package/README.md +61 -1
- package/dist/index.cjs +203 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +203 -21
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +998 -293
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +94 -4
- package/dist/react.d.ts +94 -4
- package/dist/react.js +1007 -298
- package/dist/react.js.map +1 -1
- package/dist/styles.css +223 -26
- package/dist/{toggleKind-A9v45oSb.d.cts → toggleKind-BD78LnMB.d.cts} +105 -1
- package/dist/{toggleKind-A9v45oSb.d.ts → toggleKind-BD78LnMB.d.ts} +105 -1
- package/dist/tokens.css +3 -0
- package/package.json +1 -1
package/dist/styles.css
CHANGED
|
@@ -12,37 +12,88 @@
|
|
|
12
12
|
* desktop / Codex canvas.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
/*
|
|
16
|
+
* MOTION — one contract for opening, putting away, expanding and restoring:
|
|
17
|
+
* the column's WIDTH transitions over --mxc-motion-duration on
|
|
18
|
+
* --mxc-motion-ease (default 600ms, cubic-bezier(0.4, 0, 0.2, 1) — the AI
|
|
19
|
+
* Matrx shell sidebar's slide: an even ease-in-out that travels once and
|
|
20
|
+
* stops, never a spring). The inner column keeps its width while the outer
|
|
21
|
+
* one slides, so content moves instead of reflowing. A host that reflows
|
|
22
|
+
* beside the column transitions the same property pair. Off while an edge is
|
|
23
|
+
* dragged and under prefers-reduced-motion.
|
|
24
|
+
*/
|
|
15
25
|
.mxc-column {
|
|
16
26
|
position: relative;
|
|
17
27
|
display: flex;
|
|
18
|
-
|
|
19
|
-
width:
|
|
28
|
+
justify-content: flex-end;
|
|
29
|
+
width: 0;
|
|
20
30
|
height: 100%;
|
|
21
31
|
min-height: 0;
|
|
22
32
|
flex: none;
|
|
33
|
+
overflow: hidden;
|
|
23
34
|
background: var(--mxc-frame-bg);
|
|
24
35
|
color: var(--mxc-fg);
|
|
36
|
+
isolation: isolate;
|
|
37
|
+
font-family: inherit;
|
|
38
|
+
transition: width var(--mxc-motion-duration, 600ms) var(--mxc-motion-ease, cubic-bezier(0.4, 0, 0.2, 1));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.mxc-column[data-open] {
|
|
42
|
+
width: var(--mxc-width, 640px);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.mxc-column[data-open][data-fullscreen],
|
|
46
|
+
.mxc-column[data-open][data-placement="fill"] {
|
|
47
|
+
width: 100%;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/* What slides: the column's content at its own width, right-aligned in the moving edge. */
|
|
51
|
+
.mxc-column-inner {
|
|
52
|
+
position: relative;
|
|
53
|
+
flex: none;
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
width: var(--mxc-width, 640px);
|
|
57
|
+
height: 100%;
|
|
58
|
+
min-height: 0;
|
|
25
59
|
border-left: var(--mxc-column-border-width, 0px) solid var(--mxc-border);
|
|
26
60
|
/* The column owns the top edge of the window: no header sits above it. */
|
|
27
61
|
padding-top: var(--mxc-safe-top, 0px);
|
|
28
|
-
|
|
29
|
-
font-family: inherit;
|
|
62
|
+
box-sizing: border-box;
|
|
30
63
|
}
|
|
31
64
|
|
|
32
|
-
.mxc-column[data-fullscreen],
|
|
33
|
-
.mxc-column[data-placement="fill"] {
|
|
65
|
+
.mxc-column[data-fullscreen] > .mxc-column-inner,
|
|
66
|
+
.mxc-column[data-placement="fill"] > .mxc-column-inner {
|
|
34
67
|
width: 100%;
|
|
35
68
|
border-left: 0;
|
|
36
69
|
}
|
|
37
70
|
|
|
71
|
+
/* Put away: hidden once the slide has finished (state, scroll and focus targets survive). */
|
|
72
|
+
.mxc-column:not([data-open]) > .mxc-column-inner {
|
|
73
|
+
visibility: hidden;
|
|
74
|
+
transition: visibility 0s linear var(--mxc-motion-duration, 600ms);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
:root[data-mxc-resizing] .mxc-column {
|
|
78
|
+
transition: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
@media (prefers-reduced-motion: reduce) {
|
|
82
|
+
.mxc-column,
|
|
83
|
+
.mxc-column:not([data-open]) > .mxc-column-inner {
|
|
84
|
+
transition: none;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Inside the column's left gap, so the sliding edge never clips it. */
|
|
38
89
|
.mxc-width-handle {
|
|
39
90
|
position: absolute;
|
|
40
91
|
top: 0;
|
|
41
92
|
bottom: 0;
|
|
42
|
-
left:
|
|
43
|
-
width: 6px;
|
|
93
|
+
left: 0;
|
|
94
|
+
width: max(var(--mxc-gap), 6px);
|
|
44
95
|
cursor: col-resize;
|
|
45
|
-
z-index:
|
|
96
|
+
z-index: 3;
|
|
46
97
|
touch-action: none;
|
|
47
98
|
}
|
|
48
99
|
|
|
@@ -173,6 +224,24 @@
|
|
|
173
224
|
cursor: grab;
|
|
174
225
|
}
|
|
175
226
|
|
|
227
|
+
/* One tab as a title, with its own close (a lone tab is never a trap). */
|
|
228
|
+
.mxc-single-title {
|
|
229
|
+
position: relative;
|
|
230
|
+
padding-right: 0.25rem;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.mxc-single-title > .mxc-tab-close {
|
|
234
|
+
position: static;
|
|
235
|
+
transform: none;
|
|
236
|
+
opacity: 0.7;
|
|
237
|
+
pointer-events: auto;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.mxc-single-title > .mxc-tab-close:hover,
|
|
241
|
+
.mxc-single-title > .mxc-tab-close:focus-visible {
|
|
242
|
+
opacity: 1;
|
|
243
|
+
}
|
|
244
|
+
|
|
176
245
|
.mxc-title-icon {
|
|
177
246
|
flex: none;
|
|
178
247
|
width: 0.875rem;
|
|
@@ -208,6 +277,61 @@
|
|
|
208
277
|
flex-direction: column;
|
|
209
278
|
}
|
|
210
279
|
|
|
280
|
+
/* Where the pane's bodies are placed (they live in the column's body layer). */
|
|
281
|
+
.mxc-pane-slot {
|
|
282
|
+
position: relative;
|
|
283
|
+
flex: 1 1 auto;
|
|
284
|
+
min-height: 0;
|
|
285
|
+
display: flex;
|
|
286
|
+
flex-direction: column;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/* ── the stable body layer ──────────────────────────────────────────────── */
|
|
290
|
+
|
|
291
|
+
.mxc-bodies {
|
|
292
|
+
position: absolute;
|
|
293
|
+
inset: 0;
|
|
294
|
+
z-index: 1;
|
|
295
|
+
pointer-events: none;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.mxc-item-host {
|
|
299
|
+
position: absolute;
|
|
300
|
+
left: 0;
|
|
301
|
+
top: 0;
|
|
302
|
+
width: 0;
|
|
303
|
+
height: 0;
|
|
304
|
+
display: flex;
|
|
305
|
+
flex-direction: column;
|
|
306
|
+
min-width: 0;
|
|
307
|
+
min-height: 0;
|
|
308
|
+
overflow: hidden;
|
|
309
|
+
pointer-events: auto;
|
|
310
|
+
/* The panel's bottom corners, inside its 1px border. */
|
|
311
|
+
border-radius: 0 0 max(0px, calc(var(--mxc-radius) - 1px)) max(0px, calc(var(--mxc-radius) - 1px));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* A pane rendered on its own keeps its bodies in flow. */
|
|
315
|
+
.mxc-item-host[data-inline] {
|
|
316
|
+
position: relative;
|
|
317
|
+
flex: 1 1 auto;
|
|
318
|
+
width: auto;
|
|
319
|
+
height: auto;
|
|
320
|
+
border-radius: 0;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.mxc-item-host[data-inline][data-visible="false"] {
|
|
324
|
+
display: none;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/* Hidden, never unmounted: content-visibility keeps scroll and skips rendering. */
|
|
328
|
+
.mxc-item-host[data-visible="false"],
|
|
329
|
+
.mxc-item-host[data-placed="false"] {
|
|
330
|
+
visibility: hidden;
|
|
331
|
+
content-visibility: hidden;
|
|
332
|
+
pointer-events: none;
|
|
333
|
+
}
|
|
334
|
+
|
|
211
335
|
/* ── header buttons ─────────────────────────────────────────────────────── */
|
|
212
336
|
|
|
213
337
|
.mxc-icon-button {
|
|
@@ -272,6 +396,8 @@
|
|
|
272
396
|
/* ── tabs ───────────────────────────────────────────────────────────────── */
|
|
273
397
|
|
|
274
398
|
.mxc-tabs {
|
|
399
|
+
/* The tabs' offset parent, so "keep the active tab in view" measures in strip coordinates. */
|
|
400
|
+
position: relative;
|
|
275
401
|
flex: 0 1 auto;
|
|
276
402
|
min-width: 0;
|
|
277
403
|
display: flex;
|
|
@@ -285,33 +411,46 @@
|
|
|
285
411
|
display: none;
|
|
286
412
|
}
|
|
287
413
|
|
|
414
|
+
/*
|
|
415
|
+
* TAB DENSITY (Claude Code / Codex): 12.5px text, a 14px icon, 6px side
|
|
416
|
+
* padding. Tabs shrink evenly from 8rem to a 3.25rem floor (icon + a few
|
|
417
|
+
* letters), then the strip scrolls. The active tab keeps icon + ~10 letters.
|
|
418
|
+
* The close button is OVERLAID on the tab's end and costs no width while
|
|
419
|
+
* hidden; shown (hover, active, focus) the title fades under it.
|
|
420
|
+
*/
|
|
288
421
|
.mxc-tab {
|
|
289
422
|
position: relative;
|
|
290
|
-
flex:
|
|
291
|
-
min-width:
|
|
292
|
-
max-width:
|
|
423
|
+
flex: 1 1 8rem;
|
|
424
|
+
min-width: 3.25rem;
|
|
425
|
+
max-width: 12rem;
|
|
293
426
|
display: flex;
|
|
294
427
|
align-items: center;
|
|
295
|
-
gap: 0.
|
|
296
|
-
height: 1.
|
|
297
|
-
padding: 0 0.
|
|
428
|
+
gap: 0.3125rem;
|
|
429
|
+
height: 1.625rem;
|
|
430
|
+
padding: 0 0.375rem;
|
|
298
431
|
border-radius: 0.375rem;
|
|
299
|
-
font-size: 0.
|
|
432
|
+
font-size: 0.78125rem;
|
|
300
433
|
line-height: 1rem;
|
|
301
434
|
color: var(--mxc-muted);
|
|
302
435
|
cursor: default;
|
|
303
436
|
user-select: none;
|
|
304
437
|
outline: none;
|
|
438
|
+
box-sizing: border-box;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
.mxc-tab[data-active] {
|
|
442
|
+
min-width: min(6.5rem, 100%);
|
|
443
|
+
background: var(--mxc-tab-active-bg);
|
|
444
|
+
color: var(--mxc-fg);
|
|
445
|
+
box-shadow: var(--mxc-tab-active-shadow);
|
|
305
446
|
}
|
|
306
447
|
|
|
307
448
|
.mxc-tab:hover {
|
|
308
449
|
background: var(--mxc-hover);
|
|
309
450
|
}
|
|
310
451
|
|
|
311
|
-
.mxc-tab[data-active] {
|
|
452
|
+
.mxc-tab[data-active]:hover {
|
|
312
453
|
background: var(--mxc-tab-active-bg);
|
|
313
|
-
color: var(--mxc-fg);
|
|
314
|
-
box-shadow: var(--mxc-tab-active-shadow);
|
|
315
454
|
}
|
|
316
455
|
|
|
317
456
|
.mxc-pane:not([data-focused]) .mxc-tab[data-active] {
|
|
@@ -344,10 +483,23 @@
|
|
|
344
483
|
flex: 1 1 auto;
|
|
345
484
|
min-width: 0;
|
|
346
485
|
overflow: hidden;
|
|
347
|
-
text-overflow:
|
|
486
|
+
text-overflow: clip;
|
|
348
487
|
white-space: nowrap;
|
|
349
488
|
}
|
|
350
489
|
|
|
490
|
+
/* The close shows: the title's end fades out from under it. */
|
|
491
|
+
.mxc-tab:hover .mxc-tab-title,
|
|
492
|
+
.mxc-tab[data-active] .mxc-tab-title,
|
|
493
|
+
.mxc-tab:focus-within .mxc-tab-title {
|
|
494
|
+
-webkit-mask-image: linear-gradient(to left, transparent 1.125rem, #000 1.875rem);
|
|
495
|
+
mask-image: linear-gradient(to left, transparent 1.125rem, #000 1.875rem);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.mxc-tab:not(:hover):not([data-active]):not(:focus-within) .mxc-tab-title {
|
|
499
|
+
-webkit-mask-image: linear-gradient(to left, transparent 0, #000 0.75rem);
|
|
500
|
+
mask-image: linear-gradient(to left, transparent 0, #000 0.75rem);
|
|
501
|
+
}
|
|
502
|
+
|
|
351
503
|
.mxc-tab-note {
|
|
352
504
|
flex: none;
|
|
353
505
|
font-size: 0.6875rem;
|
|
@@ -355,7 +507,10 @@
|
|
|
355
507
|
}
|
|
356
508
|
|
|
357
509
|
.mxc-tab-close {
|
|
358
|
-
|
|
510
|
+
position: absolute;
|
|
511
|
+
top: 50%;
|
|
512
|
+
right: 0.1875rem;
|
|
513
|
+
transform: translateY(-50%);
|
|
359
514
|
display: inline-flex;
|
|
360
515
|
align-items: center;
|
|
361
516
|
justify-content: center;
|
|
@@ -367,6 +522,7 @@
|
|
|
367
522
|
background: transparent;
|
|
368
523
|
color: var(--mxc-muted);
|
|
369
524
|
opacity: 0;
|
|
525
|
+
pointer-events: none;
|
|
370
526
|
cursor: pointer;
|
|
371
527
|
outline: none;
|
|
372
528
|
}
|
|
@@ -380,6 +536,7 @@
|
|
|
380
536
|
.mxc-tab[data-active] .mxc-tab-close,
|
|
381
537
|
.mxc-tab-close:focus-visible {
|
|
382
538
|
opacity: 1;
|
|
539
|
+
pointer-events: auto;
|
|
383
540
|
}
|
|
384
541
|
|
|
385
542
|
.mxc-tab-close:hover {
|
|
@@ -391,6 +548,14 @@
|
|
|
391
548
|
box-shadow: 0 0 0 2px var(--mxc-ring);
|
|
392
549
|
}
|
|
393
550
|
|
|
551
|
+
/* Touch screens have no hover: every tab's close is reachable. */
|
|
552
|
+
@media (pointer: coarse) {
|
|
553
|
+
.mxc-tab .mxc-tab-close {
|
|
554
|
+
opacity: 1;
|
|
555
|
+
pointer-events: auto;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
394
559
|
/* ── browser bar ────────────────────────────────────────────────────────── */
|
|
395
560
|
|
|
396
561
|
/* In a body column it keeps its own height; in a header's leading slot it fills the width. */
|
|
@@ -550,11 +715,25 @@
|
|
|
550
715
|
|
|
551
716
|
.mxc-launcher {
|
|
552
717
|
margin: auto;
|
|
718
|
+
display: flex;
|
|
719
|
+
flex-direction: column;
|
|
720
|
+
gap: 1rem;
|
|
721
|
+
width: min(32rem, 100%);
|
|
722
|
+
padding: 1.5rem;
|
|
723
|
+
box-sizing: border-box;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
.mxc-launcher-heading {
|
|
727
|
+
margin: 0 0 0.375rem;
|
|
728
|
+
font-size: 0.6875rem;
|
|
729
|
+
font-weight: 500;
|
|
730
|
+
color: var(--mxc-muted);
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
.mxc-launcher-grid {
|
|
553
734
|
display: grid;
|
|
554
735
|
grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
|
|
555
736
|
gap: 0.5rem;
|
|
556
|
-
width: min(32rem, 100%);
|
|
557
|
-
padding: 1.5rem;
|
|
558
737
|
}
|
|
559
738
|
|
|
560
739
|
.mxc-launcher-item {
|
|
@@ -691,6 +870,12 @@
|
|
|
691
870
|
pointer-events: none;
|
|
692
871
|
}
|
|
693
872
|
|
|
873
|
+
/* CanvasKindIcons — a header's tool cluster: tap targets touching, like every header set. */
|
|
874
|
+
.mxc-kind-icons {
|
|
875
|
+
display: inline-flex;
|
|
876
|
+
align-items: center;
|
|
877
|
+
}
|
|
878
|
+
|
|
694
879
|
/* CanvasFrame — for hosts without their own shell. */
|
|
695
880
|
.mxc-frame {
|
|
696
881
|
display: flex;
|
|
@@ -725,16 +910,28 @@
|
|
|
725
910
|
display: none !important;
|
|
726
911
|
}
|
|
727
912
|
|
|
913
|
+
/* Slides in from the right edge, the full width of the screen. */
|
|
728
914
|
.mxc-column[data-placement="docked"] {
|
|
729
915
|
position: fixed;
|
|
730
|
-
|
|
916
|
+
top: 0;
|
|
917
|
+
right: 0;
|
|
918
|
+
bottom: 0;
|
|
919
|
+
left: auto;
|
|
731
920
|
z-index: var(--mxc-mobile-z, 60);
|
|
921
|
+
width: 0;
|
|
922
|
+
--mxc-gap: 0px;
|
|
923
|
+
--mxc-radius: 0px;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
.mxc-column[data-placement="docked"][data-open] {
|
|
732
927
|
width: 100%;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
.mxc-column[data-placement="docked"] > .mxc-column-inner {
|
|
931
|
+
width: 100vw;
|
|
733
932
|
border-left: 0;
|
|
734
933
|
padding-top: env(safe-area-inset-top, 0px);
|
|
735
934
|
padding-bottom: env(safe-area-inset-bottom, 0px);
|
|
736
|
-
--mxc-gap: 0px;
|
|
737
|
-
--mxc-radius: 0px;
|
|
738
935
|
}
|
|
739
936
|
|
|
740
937
|
.mxc-column[data-placement="docked"] .mxc-panel {
|
|
@@ -37,6 +37,20 @@ type CanvasWindowId = string & {
|
|
|
37
37
|
readonly [windowIdBrand]: true;
|
|
38
38
|
};
|
|
39
39
|
declare const CANVAS_MAIN_WINDOW: CanvasWindowId;
|
|
40
|
+
/**
|
|
41
|
+
* Where a tab was before it navigated in place (`controller.navigate`). Plain
|
|
42
|
+
* JSON, so history persists with the layout; Back reopens the last entry.
|
|
43
|
+
*/
|
|
44
|
+
interface CanvasHistoryEntry {
|
|
45
|
+
readonly kind: string;
|
|
46
|
+
readonly key: string;
|
|
47
|
+
readonly title: string | null;
|
|
48
|
+
readonly data: CanvasJson;
|
|
49
|
+
}
|
|
50
|
+
/** The built-in "New tab" item: the launcher, shown as a tab. Never registered as a kind. */
|
|
51
|
+
declare const CANVAS_LAUNCHER_KIND = "canvas.launcher";
|
|
52
|
+
/** How many steps Back can go in one tab. Older entries fall off. */
|
|
53
|
+
declare const CANVAS_HISTORY_LIMIT = 50;
|
|
40
54
|
interface CanvasItem {
|
|
41
55
|
readonly id: CanvasItemId;
|
|
42
56
|
/** Registered kind id (see `registerCanvasKind`). */
|
|
@@ -48,6 +62,14 @@ interface CanvasItem {
|
|
|
48
62
|
readonly data: CanvasJson;
|
|
49
63
|
readonly openedAt: number;
|
|
50
64
|
readonly updatedAt: number;
|
|
65
|
+
/** Earlier contents of this tab, oldest first (navigate in place). Absent = no Back. */
|
|
66
|
+
readonly history?: readonly CanvasHistoryEntry[] | undefined;
|
|
67
|
+
/**
|
|
68
|
+
* The mounted body's identity. Minted when the item is created and kept by
|
|
69
|
+
* `rekey`, so a draft that receives its durable id keeps its live body.
|
|
70
|
+
* Absent on items saved before 0.5.0 (the item id stands in).
|
|
71
|
+
*/
|
|
72
|
+
readonly instance?: string | undefined;
|
|
51
73
|
}
|
|
52
74
|
interface CanvasPane {
|
|
53
75
|
readonly id: CanvasPaneId;
|
|
@@ -122,6 +144,15 @@ interface CanvasState extends CanvasWindowFrame {
|
|
|
122
144
|
type CanvasOpenTarget = "focused" | "split-right" | "split-down" | {
|
|
123
145
|
readonly paneId: CanvasPaneId;
|
|
124
146
|
};
|
|
147
|
+
/** Where a NEW item of a kind prefers to open while the canvas already shows something. */
|
|
148
|
+
type CanvasPreferredTarget = "focused" | "split-right" | "split-down";
|
|
149
|
+
/** What a tab navigates to in place. */
|
|
150
|
+
interface CanvasNavigateInput {
|
|
151
|
+
readonly kind: string;
|
|
152
|
+
readonly key: string;
|
|
153
|
+
readonly title?: string | null | undefined;
|
|
154
|
+
readonly data?: CanvasJson | undefined;
|
|
155
|
+
}
|
|
125
156
|
interface CanvasOpenInput {
|
|
126
157
|
readonly kind: string;
|
|
127
158
|
/** Which window a NEW item opens in. Default: the controller's window (main). An existing item stays where it is. */
|
|
@@ -184,6 +215,19 @@ type CanvasAction = {
|
|
|
184
215
|
itemId: CanvasItemId;
|
|
185
216
|
key: string;
|
|
186
217
|
};
|
|
218
|
+
} | {
|
|
219
|
+
type: `${typeof P}navigate`;
|
|
220
|
+
payload: {
|
|
221
|
+
itemId: CanvasItemId;
|
|
222
|
+
to: CanvasNavigateInput;
|
|
223
|
+
now: number;
|
|
224
|
+
};
|
|
225
|
+
} | {
|
|
226
|
+
type: `${typeof P}back`;
|
|
227
|
+
payload: {
|
|
228
|
+
itemId: CanvasItemId;
|
|
229
|
+
now: number;
|
|
230
|
+
};
|
|
187
231
|
} | {
|
|
188
232
|
type: `${typeof P}closeItem`;
|
|
189
233
|
payload: {
|
|
@@ -288,6 +332,10 @@ declare const canvasActions: {
|
|
|
288
332
|
}) => CanvasAction;
|
|
289
333
|
/** Gives an item a new identity in place (e.g. a draft that was just saved and now has an id). */
|
|
290
334
|
readonly rekey: (itemId: CanvasItemId, key: string) => CanvasAction;
|
|
335
|
+
/** Replaces a tab's content with another item in place, remembering what it showed (Back returns). */
|
|
336
|
+
readonly navigate: (itemId: CanvasItemId, to: CanvasNavigateInput) => CanvasAction;
|
|
337
|
+
/** Returns a tab to what it showed before its last navigate. */
|
|
338
|
+
readonly back: (itemId: CanvasItemId) => CanvasAction;
|
|
291
339
|
readonly closeItem: (itemId: CanvasItemId) => CanvasAction;
|
|
292
340
|
readonly closeOthers: (itemId: CanvasItemId) => CanvasAction;
|
|
293
341
|
readonly activate: (itemId: CanvasItemId) => CanvasAction;
|
|
@@ -521,6 +569,11 @@ interface CanvasControllerOptions {
|
|
|
521
569
|
readonly windowPorts?: (() => CanvasWindowPorts) | undefined;
|
|
522
570
|
/** Session hooks for a kind, or undefined when the kind is not session-backed. */
|
|
523
571
|
readonly sessions?: ((kind: string) => CanvasSessionHooks | undefined) | undefined;
|
|
572
|
+
/**
|
|
573
|
+
* Where a NEW item of a kind opens while the canvas already shows something
|
|
574
|
+
* (a kind's `preferredTarget`). An explicit `target` always wins.
|
|
575
|
+
*/
|
|
576
|
+
readonly preferredTarget?: ((kind: string) => CanvasPreferredTarget | undefined) | undefined;
|
|
524
577
|
}
|
|
525
578
|
interface CanvasController {
|
|
526
579
|
readonly store: CanvasStoreBinding;
|
|
@@ -537,6 +590,17 @@ interface CanvasController {
|
|
|
537
590
|
}): boolean;
|
|
538
591
|
/** Gives an item a new identity in place (a draft that was saved and now has a durable id). */
|
|
539
592
|
rekey(itemId: CanvasItemId, key: string): CanvasItemId | null;
|
|
593
|
+
/**
|
|
594
|
+
* Replaces a tab's content with another item IN PLACE and records history,
|
|
595
|
+
* so the pane header offers Back. Identity still holds: when the target is
|
|
596
|
+
* already open, it is focused where it is (a launcher tab then closes; any
|
|
597
|
+
* other tab keeps what it shows). Returns the item now shown, or null.
|
|
598
|
+
*/
|
|
599
|
+
navigate(itemId: CanvasItemId, to: CanvasNavigateInput): CanvasItemId | null;
|
|
600
|
+
/** Returns a tab to what it showed before (see navigate). Null when it has no history. */
|
|
601
|
+
back(itemId: CanvasItemId): CanvasItemId | null;
|
|
602
|
+
/** Opens the launcher as a new tab in a pane (default: this window's focused pane), or focuses the one already there. */
|
|
603
|
+
newTab(paneId?: CanvasPaneId): CanvasItemId;
|
|
540
604
|
close(itemId: CanvasItemId): void;
|
|
541
605
|
closeOthers(itemId: CanvasItemId): void;
|
|
542
606
|
activate(itemId: CanvasItemId): void;
|
|
@@ -573,6 +637,46 @@ interface CanvasController {
|
|
|
573
637
|
}
|
|
574
638
|
declare function createCanvasController(options: CanvasControllerOptions): CanvasController;
|
|
575
639
|
|
|
640
|
+
/**
|
|
641
|
+
* Presentation — what a body knows about the space the canvas gives it.
|
|
642
|
+
*
|
|
643
|
+
* A body rendered in the canvas is told so, and told its live shape: a tall
|
|
644
|
+
* narrow pane is not a wide page. Content that lays itself out in a direction
|
|
645
|
+
* (a graph, a timeline, a row of cards) reads `orientation` and flows down a
|
|
646
|
+
* portrait pane instead of across it. Pure data and pure functions, so a
|
|
647
|
+
* desktop main process can compute the same answer for a native view.
|
|
648
|
+
*/
|
|
649
|
+
type CanvasPresentationOrientation = "portrait" | "landscape" | "square";
|
|
650
|
+
interface CanvasPresentation {
|
|
651
|
+
/** Always "canvas" — a body rendered anywhere else gets no presentation at all. */
|
|
652
|
+
readonly host: "canvas";
|
|
653
|
+
/** The body's size in CSS pixels, live. */
|
|
654
|
+
readonly width: number;
|
|
655
|
+
readonly height: number;
|
|
656
|
+
readonly orientation: CanvasPresentationOrientation;
|
|
657
|
+
/** Narrower than `CANVAS_NARROW_WIDTH`: one column of content at most. */
|
|
658
|
+
readonly isNarrow: boolean;
|
|
659
|
+
/** The canvas is expanded to fill the space its host gives it. */
|
|
660
|
+
readonly isFullscreen: boolean;
|
|
661
|
+
/** How many panes the window's canvas shows right now. */
|
|
662
|
+
readonly paneCount: number;
|
|
663
|
+
}
|
|
664
|
+
/** Under this width (px) a body is narrow. */
|
|
665
|
+
declare const CANVAS_NARROW_WIDTH = 480;
|
|
666
|
+
declare function canvasOrientation(width: number, height: number): CanvasPresentationOrientation;
|
|
667
|
+
declare function canvasPresentation(input: {
|
|
668
|
+
readonly width: number;
|
|
669
|
+
readonly height: number;
|
|
670
|
+
readonly isFullscreen: boolean;
|
|
671
|
+
readonly paneCount: number;
|
|
672
|
+
}): CanvasPresentation;
|
|
673
|
+
/**
|
|
674
|
+
* Which way content with a layout direction should flow: "vertical" (top to
|
|
675
|
+
* bottom) in a portrait or narrow body, "horizontal" (left to right) otherwise
|
|
676
|
+
* and outside the canvas.
|
|
677
|
+
*/
|
|
678
|
+
declare function preferredFlowDirection(presentation: CanvasPresentation | null | undefined): "vertical" | "horizontal";
|
|
679
|
+
|
|
576
680
|
/**
|
|
577
681
|
* "Toggle or focus this kind" — what a toolbar icon does (the desktop's
|
|
578
682
|
* Terminal/Review/Browser icons, the web's quick-tool launchers):
|
|
@@ -602,4 +706,4 @@ interface ToggleKindInput {
|
|
|
602
706
|
/** Returns what the item is after the click. */
|
|
603
707
|
declare function toggleKind(controller: CanvasController, input: ToggleKindInput): CanvasKindVisibility;
|
|
604
708
|
|
|
605
|
-
export {
|
|
709
|
+
export { canvasPresentation as $, type CanvasErrorSink as A, type CanvasKeyValueStore as B, type CanvasItemId as C, type CanvasKindVisibility as D, type CanvasLayoutRules as E, type CanvasNavigateInput as F, type CanvasOpenInput as G, type CanvasOpenTarget as H, type CanvasOrientation as I, type CanvasPersistencePort as J, type CanvasPreferredTarget as K, type CanvasPresentation as L, type CanvasPresentationOrientation as M, type CanvasRect as N, type CanvasSessionAttachReason as O, type CanvasSessionDetachReason as P, type CanvasSessionEvent as Q, type CanvasSessionHooks as R, type CanvasSplitId as S, type CanvasWindowCloseMode as T, type CanvasWindowFrame as U, type CanvasWindowPorts as V, type ReduxStoreLike as W, type ToggleKindInput as X, bindCanvasToReduxStore as Y, canvasActions as Z, canvasOrientation as _, type CanvasWindowId as a, canvasReducer as a0, canvasWindowFrame as a1, canvasWindowOfPane as a2, clampDraggedCanvasWidth as a3, consoleCanvasErrorSink as a4, createCanvasController as a5, createCanvasStore as a6, createInitialCanvasState as a7, createKeyValueCanvasPersistence as a8, createLocalStorageCanvasPersistence as a9, defaultCanvasWidth as aa, fitCanvasWidth as ab, foldCanvasWindows as ac, isCanvasAction as ad, listCanvasWindowIds as ae, preferredFlowDirection as af, resolveCanvasLayoutRules as ag, sanitizeCanvasSnapshot as ah, selectCanvasKindVisibility as ai, shouldStackSplit as aj, toPersistableSnapshot as ak, toggleKind as al, type CanvasJson as b, type CanvasLayoutNode as c, type CanvasPaneId as d, type CanvasStoreBinding as e, type CanvasState as f, type CanvasAction as g, type CanvasController as h, type CanvasItem as i, type CanvasExtraWindow as j, type CanvasHistoryEntry as k, type CanvasPane as l, canvasWindowOfItem as m, CANVAS_DEFAULT_KEY as n, CANVAS_DEFAULT_WIDTH as o, CANVAS_DESKTOP_LAYOUT_RULES as p, CANVAS_HISTORY_LIMIT as q, CANVAS_LAUNCHER_KIND as r, CANVAS_MAIN_WINDOW as s, CANVAS_MIN_SPLIT_FRACTION as t, CANVAS_MIN_WIDTH as u, CANVAS_NARROW_WIDTH as v, CANVAS_STORAGE_KEY as w, CANVAS_WEB_LAYOUT_RULES as x, type CanvasControllerOptions as y, type CanvasErrorReport as z };
|