@ai-matrx/canvas 0.5.3 → 0.6.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 +27 -0
- package/README.md +6 -0
- package/dist/index.cjs +45 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +45 -9
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +247 -59
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +251 -62
- package/dist/react.js.map +1 -1
- package/dist/styles.css +66 -27
- package/dist/{toggleKind-BD78LnMB.d.cts → toggleKind-DhDmWExm.d.cts} +27 -1
- package/dist/{toggleKind-BD78LnMB.d.ts → toggleKind-DhDmWExm.d.ts} +27 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
Live measurement of 0.6.0 (a 756px pane, five tabs, Quick Chat's header actions present):
|
|
6
|
+
|
|
7
|
+
- **The strip takes the header's free width** (`flex: 1`). Sized to its own intrinsic width, the browser undercut every text tab by 2–3px and cut each name; now tabs lay out in real room ("+" sits at the strip's end).
|
|
8
|
+
- **No ellipsis.** System fonts overflow their intrinsic width by a fraction of a pixel, which put "…" on names that fit ("Quick Ch…"). Titles clip; a name really cut (`data-truncated` on the tab, measured: more than 1px) fades at its end.
|
|
9
|
+
- **Room for names:** only the active tab reserves its (always shown) close's room — a background tab's close overlays on hover with the name fading under it; icon gap 4px; an empty leading slot takes no room and no gap (the actions keep the end with `margin-left: auto`).
|
|
10
|
+
|
|
11
|
+
### Consumer action
|
|
12
|
+
|
|
13
|
+
- None.
|
|
14
|
+
|
|
15
|
+
## 0.6.0
|
|
16
|
+
|
|
17
|
+
Independent live sweep (admin, clone):
|
|
18
|
+
|
|
19
|
+
- **Tabs read fully.** A 751px pane showed "Quic", "Messa" while 363px of header sat empty: `.mxc-tab`'s `container: inline-size` made every tab's intrinsic width ignore its text, so the strip sized to nothing. Tabs are now as wide as their names (max 12rem), shrink evenly only when the header is out of room (floor 4.5rem), then the strip scrolls with a visible cue — the clipped edge fades and "Scroll tabs left/right" chevrons page it. The active tab stays in view. Every tab reserves its close's room, so a name is never under the button (ellipsis when squeezed). The narrow-tab icon drop (container query) is gone.
|
|
20
|
+
- **Back keeps the body.** History entries carry the body's `instance`; Back returns the SAME live body, and the content Back leaves is kept in `CanvasItem.kept` (newest last) — navigating to it again revives its body ("+" → Quick Chat → type → Back → Quick Chat finds the draft). The newest `CANVAS_KEEP_ALIVE_DEPTH` (5) back steps and the kept steps stay mounted-hidden in the body layer; closing the tab disposes them. Bodies are ordered by birth, so Back never reorders the DOM.
|
|
21
|
+
- **Phones present one pane.** At `CANVAS_PHONE_MAX_WIDTH` (767px) a restored multi-pane layout shows ONE pane (the focused one) whose strip carries every pane's tabs; choosing another pane's tab presents that pane. The layout tree is untouched.
|
|
22
|
+
- **`CanvasToggle` is a pressed toggle** (`pressed` → `aria-pressed` + the tap-target pressed look) while the canvas is open.
|
|
23
|
+
- **Double-clicking a launcher item** no longer lands in the tab it opens: the rest of that multi-click is swallowed; launcher items are `user-select: none`.
|
|
24
|
+
|
|
25
|
+
### Consumer action
|
|
26
|
+
|
|
27
|
+
- Code that builds `CanvasHistoryEntry` objects itself may pass `instance`; code that reads `CanvasItem` can ignore the new `kept`.
|
|
28
|
+
- CSS that targeted `.mxc-tabs` as the header's direct child: the strip is now `.mxc-tabstrip > .mxc-tabs`.
|
|
29
|
+
|
|
3
30
|
## 0.5.3
|
|
4
31
|
|
|
5
32
|
- A kind's `HeaderAction` renders in its own gapless row (`.mxc-kind-actions`): tap-target buttons space themselves, and the pane's 2px action gap tripped the tap-target misuse guard over the tab strip (seen live with Quick Chat).
|
package/README.md
CHANGED
|
@@ -62,6 +62,12 @@ canvas.back(item.id);
|
|
|
62
62
|
|
|
63
63
|
Navigating to something already open elsewhere focuses it there (a launcher tab closes; any other tab keeps what it shows). History is plain JSON and persists. A lone tab shown as a title keeps its own close.
|
|
64
64
|
|
|
65
|
+
**Back never throws a body away.** The newest `CANVAS_KEEP_ALIVE_DEPTH` (5) steps Back can reach, and the 5 most recent contents Back stepped away from (`item.kept`), keep their bodies mounted and hidden. Back returns the SAME live body; choosing a content Back left (Quick Chat again from the launcher) revives its body — a typed draft is still there. Older steps still work but mount fresh. Closing the tab disposes every kept body.
|
|
66
|
+
|
|
67
|
+
**The strip.** A tab is as wide as its name (up to 12rem); tabs shrink evenly only when the header runs out of room (floor 4.5rem), then the strip scrolls — the clipped edge fades and chevrons page it. The active tab stays in view.
|
|
68
|
+
|
|
69
|
+
**Phones** (`CANVAS_PHONE_MAX_WIDTH`, 767px): the column presents ONE pane — the focused one — whose strip carries every pane's tabs; choosing a tab from another pane presents that pane. The layout tree is untouched, so a wider screen gets its split back.
|
|
70
|
+
|
|
65
71
|
## A page's own launcher entries
|
|
66
72
|
|
|
67
73
|
```tsx
|
package/dist/index.cjs
CHANGED
|
@@ -24,11 +24,13 @@ __export(src_exports, {
|
|
|
24
24
|
CANVAS_DEFAULT_WIDTH: () => CANVAS_DEFAULT_WIDTH,
|
|
25
25
|
CANVAS_DESKTOP_LAYOUT_RULES: () => CANVAS_DESKTOP_LAYOUT_RULES,
|
|
26
26
|
CANVAS_HISTORY_LIMIT: () => CANVAS_HISTORY_LIMIT,
|
|
27
|
+
CANVAS_KEEP_ALIVE_DEPTH: () => CANVAS_KEEP_ALIVE_DEPTH,
|
|
27
28
|
CANVAS_LAUNCHER_KIND: () => CANVAS_LAUNCHER_KIND,
|
|
28
29
|
CANVAS_MAIN_WINDOW: () => CANVAS_MAIN_WINDOW,
|
|
29
30
|
CANVAS_MIN_SPLIT_FRACTION: () => CANVAS_MIN_SPLIT_FRACTION,
|
|
30
31
|
CANVAS_MIN_WIDTH: () => CANVAS_MIN_WIDTH,
|
|
31
32
|
CANVAS_NARROW_WIDTH: () => CANVAS_NARROW_WIDTH,
|
|
33
|
+
CANVAS_PHONE_MAX_WIDTH: () => CANVAS_PHONE_MAX_WIDTH,
|
|
32
34
|
CANVAS_SHORTCUTS: () => CANVAS_SHORTCUTS,
|
|
33
35
|
CANVAS_STORAGE_KEY: () => CANVAS_STORAGE_KEY,
|
|
34
36
|
CANVAS_WEB_LAYOUT_RULES: () => CANVAS_WEB_LAYOUT_RULES,
|
|
@@ -95,6 +97,7 @@ module.exports = __toCommonJS(src_exports);
|
|
|
95
97
|
var CANVAS_MAIN_WINDOW = "main";
|
|
96
98
|
var CANVAS_LAUNCHER_KIND = "canvas.launcher";
|
|
97
99
|
var CANVAS_HISTORY_LIMIT = 50;
|
|
100
|
+
var CANVAS_KEEP_ALIVE_DEPTH = 5;
|
|
98
101
|
var CANVAS_MIN_WIDTH = 360;
|
|
99
102
|
var CANVAS_DEFAULT_WIDTH = 640;
|
|
100
103
|
var CANVAS_MIN_SPLIT_FRACTION = 0.12;
|
|
@@ -460,8 +463,10 @@ function sendHome(state, itemId, home, activate) {
|
|
|
460
463
|
next = insertInto(next, homeFrame.focusedPaneId, itemId, activate);
|
|
461
464
|
return activate ? withFrame(next, target, { focusedPaneId: homeFrame.focusedPaneId }) : next;
|
|
462
465
|
}
|
|
463
|
-
function newItem(kind, key, title, data, now,
|
|
466
|
+
function newItem(kind, key, title, data, now, steps) {
|
|
464
467
|
const id = canvasItemId(kind, key);
|
|
468
|
+
const history = steps?.history ?? [];
|
|
469
|
+
const kept = steps?.kept ?? [];
|
|
465
470
|
return {
|
|
466
471
|
id,
|
|
467
472
|
kind,
|
|
@@ -470,12 +475,14 @@ function newItem(kind, key, title, data, now, history) {
|
|
|
470
475
|
data,
|
|
471
476
|
openedAt: now,
|
|
472
477
|
updatedAt: now,
|
|
473
|
-
instance
|
|
474
|
-
|
|
478
|
+
// A reused instance is the SAME live body coming back (Back, or returning to a kept step).
|
|
479
|
+
instance: steps?.instance ?? `${now.toString(36)}:${id}`,
|
|
480
|
+
...history.length > 0 ? { history: history.slice(-CANVAS_HISTORY_LIMIT) } : {},
|
|
481
|
+
...kept.length > 0 ? { kept: kept.slice(-CANVAS_KEEP_ALIVE_DEPTH) } : {}
|
|
475
482
|
};
|
|
476
483
|
}
|
|
477
484
|
function historyEntry(item) {
|
|
478
|
-
return { kind: item.kind, key: item.key, title: item.title, data: item.data };
|
|
485
|
+
return { kind: item.kind, key: item.key, title: item.title, data: item.data, ...item.instance ? { instance: item.instance } : {} };
|
|
479
486
|
}
|
|
480
487
|
function replaceInPlace(state, current, next) {
|
|
481
488
|
const pane = paneOf(state, current.id);
|
|
@@ -504,17 +511,25 @@ function sanitizeHistory(raw) {
|
|
|
504
511
|
if (!entry || typeof entry !== "object") continue;
|
|
505
512
|
const e = entry;
|
|
506
513
|
if (typeof e.kind !== "string" || typeof e.key !== "string") continue;
|
|
507
|
-
out.push({
|
|
514
|
+
out.push({
|
|
515
|
+
kind: e.kind,
|
|
516
|
+
key: e.key,
|
|
517
|
+
title: typeof e.title === "string" ? e.title : null,
|
|
518
|
+
data: e.data ?? null,
|
|
519
|
+
...typeof e.instance === "string" && e.instance ? { instance: e.instance } : {}
|
|
520
|
+
});
|
|
508
521
|
}
|
|
509
522
|
return out.slice(-CANVAS_HISTORY_LIMIT);
|
|
510
523
|
}
|
|
511
524
|
function sanitizeItem(item) {
|
|
512
525
|
const history = sanitizeHistory(item.history);
|
|
513
|
-
const
|
|
526
|
+
const kept = sanitizeHistory(item.kept).slice(-CANVAS_KEEP_ALIVE_DEPTH);
|
|
527
|
+
const { history: _history, kept: _kept, instance, ...rest } = item;
|
|
514
528
|
return {
|
|
515
529
|
...rest,
|
|
516
530
|
...typeof instance === "string" && instance ? { instance } : {},
|
|
517
|
-
...history.length > 0 ? { history } : {}
|
|
531
|
+
...history.length > 0 ? { history } : {},
|
|
532
|
+
...kept.length > 0 ? { kept } : {}
|
|
518
533
|
};
|
|
519
534
|
}
|
|
520
535
|
function clampWidth(width) {
|
|
@@ -701,7 +716,22 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
701
716
|
return current.kind === CANVAS_LAUNCHER_KIND ? canvasReducer(focused, canvasActions.closeItem(itemId)) : focused;
|
|
702
717
|
}
|
|
703
718
|
const history = [...current.history ?? [], historyEntry(current)];
|
|
704
|
-
|
|
719
|
+
const keptAll = current.kept ?? [];
|
|
720
|
+
const keptIndex = keptAll.findIndex((entry) => canvasItemId(entry.kind, entry.key) === targetId);
|
|
721
|
+
const revived = keptIndex >= 0 ? keptAll[keptIndex] : void 0;
|
|
722
|
+
const kept = keptAll.filter((_, i) => i !== keptIndex);
|
|
723
|
+
return replaceInPlace(
|
|
724
|
+
state,
|
|
725
|
+
current,
|
|
726
|
+
newItem(
|
|
727
|
+
to.kind,
|
|
728
|
+
to.key,
|
|
729
|
+
to.title !== void 0 ? to.title : revived?.title ?? null,
|
|
730
|
+
to.data !== void 0 ? to.data : revived?.data ?? null,
|
|
731
|
+
now,
|
|
732
|
+
{ history, kept, instance: revived?.instance }
|
|
733
|
+
)
|
|
734
|
+
);
|
|
705
735
|
}
|
|
706
736
|
case `${P}back`: {
|
|
707
737
|
const { itemId, now } = action.payload;
|
|
@@ -714,7 +744,12 @@ function canvasReducer(state = createInitialCanvasState(), action) {
|
|
|
714
744
|
if (state.items[targetId]) {
|
|
715
745
|
return focusExisting(canvasReducer(state, canvasActions.closeItem(itemId)), targetId);
|
|
716
746
|
}
|
|
717
|
-
|
|
747
|
+
const kept = [...(current.kept ?? []).filter((k) => canvasItemId(k.kind, k.key) !== itemId), historyEntry(current)];
|
|
748
|
+
return replaceInPlace(
|
|
749
|
+
state,
|
|
750
|
+
current,
|
|
751
|
+
newItem(entry.kind, entry.key, entry.title, entry.data, now, { history: history.slice(0, -1), kept, instance: entry.instance })
|
|
752
|
+
);
|
|
718
753
|
}
|
|
719
754
|
case `${P}closeItem`: {
|
|
720
755
|
const { itemId } = action.payload;
|
|
@@ -1551,6 +1586,7 @@ function selectCanvasItemHistory(s, itemId) {
|
|
|
1551
1586
|
var selectCanvasCanGoBack = (s, itemId) => (s.items[itemId]?.history?.length ?? 0) > 0;
|
|
1552
1587
|
|
|
1553
1588
|
// src/core/presentation.ts
|
|
1589
|
+
var CANVAS_PHONE_MAX_WIDTH = 767;
|
|
1554
1590
|
var CANVAS_NARROW_WIDTH = 480;
|
|
1555
1591
|
var SQUARE_BAND = 1.2;
|
|
1556
1592
|
function canvasOrientation(width, height) {
|