@neta-art/cohub 11.1.0 → 11.3.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.
- package/dist/board/editor/context.d.ts +1 -0
- package/dist/board/editor/context.js +1 -0
- package/dist/board/editor/editor.d.ts +1 -0
- package/dist/board/editor/editor.js +7 -0
- package/dist/board/editor/interaction.d.ts +4 -0
- package/dist/board/editor/interaction.js +16 -7
- package/dist/board/editor/selection-transform.d.ts +1 -1
- package/dist/board/editor/selection-transform.js +13 -2
- package/dist/board/export/index.d.ts +2 -0
- package/dist/board/export/index.js +1 -0
- package/dist/board/export/scene.d.ts +2 -0
- package/dist/board/export/scene.js +4 -0
- package/dist/board/model/file-preview.d.ts +0 -1
- package/dist/board/model/file-preview.js +10 -1
- package/dist/board/model/index.d.ts +2 -1
- package/dist/board/model/index.js +2 -1
- package/dist/board/model/script-api.d.ts +69 -0
- package/dist/board/model/script-api.js +77 -0
- package/dist/board/render/audio-waveform.d.ts +1 -1
- package/dist/board/render/audio-waveform.js +8 -5
- package/dist/board/render/card-tag.d.ts +24 -0
- package/dist/board/render/card-tag.js +205 -0
- package/dist/board/render/index.d.ts +7 -4
- package/dist/board/render/index.js +7 -4
- package/dist/board/render/media-interaction.d.ts +2 -1
- package/dist/board/render/media-interaction.js +14 -6
- package/dist/board/render/palette.js +2 -2
- package/dist/board/render/renderers/arrow-card-renderer.js +20 -16
- package/dist/board/render/renderers/audio-card-renderer.d.ts +8 -1
- package/dist/board/render/renderers/audio-card-renderer.js +64 -60
- package/dist/board/render/renderers/base-card-renderer.d.ts +1 -26
- package/dist/board/render/renderers/base-card-renderer.js +6 -92
- package/dist/board/render/renderers/board-renderer-registry.d.ts +8 -0
- package/dist/board/render/renderers/board-renderer-registry.js +3 -3
- package/dist/board/render/renderers/custom-card-renderer.js +4 -7
- package/dist/board/render/renderers/effect-card-renderer.js +0 -5
- package/dist/board/render/renderers/file-card-renderer.d.ts +1 -11
- package/dist/board/render/renderers/file-card-renderer.js +43 -105
- package/dist/board/render/renderers/frame-card-renderer.js +14 -15
- package/dist/board/render/renderers/image-card-renderer.js +62 -67
- package/dist/board/render/renderers/media-key.d.ts +6 -0
- package/dist/board/render/renderers/media-key.js +39 -0
- package/dist/board/render/renderers/shape-card-renderer.js +6 -12
- package/dist/board/render/renderers/task-card-renderer.d.ts +1 -9
- package/dist/board/render/renderers/task-card-renderer.js +82 -56
- package/dist/board/render/renderers/unknown-card-renderer.js +17 -16
- package/dist/board/render/renderers/video-card-renderer.js +73 -80
- package/dist/board/render/stroke.d.ts +1 -1
- package/dist/board/render/stroke.js +5 -4
- package/dist/board/render/style.d.ts +29 -0
- package/dist/board/render/style.js +54 -0
- package/dist/board/render/surface.d.ts +34 -0
- package/dist/board/render/surface.js +132 -0
- package/dist/board/render/text-fit.d.ts +8 -0
- package/dist/board/render/text-fit.js +60 -0
- package/dist/board/render/text-resolution.d.ts +2 -1
- package/dist/board/render/text-resolution.js +4 -1
- package/dist/board/stage/index.d.ts +3 -2
- package/dist/board/stage/index.js +2 -1
- package/dist/board/stage/overlay.d.ts +41 -0
- package/dist/board/stage/overlay.js +182 -0
- package/dist/board/stage/scene.d.ts +2 -43
- package/dist/board/stage/scene.js +23 -131
- package/dist/board/stage/stage.d.ts +2 -0
- package/dist/board/stage/stage.js +99 -32
- package/dist/chunks/app-realtime.js +1697 -0
- package/dist/chunks/environment.d.ts +997 -3
- package/dist/chunks/http.d.ts +393 -3
- package/dist/chunks/http.js +40 -1
- package/dist/chunks/websocket.d.ts +4 -0
- package/dist/chunks/websocket.js +11 -2
- package/dist/http.d.ts +2 -2
- package/dist/index.d.ts +4 -323
- package/dist/index.js +58 -1660
- package/dist/protocol/dist/board-model.js +51 -51
- package/dist/protocol/dist/board-schema.d.ts +1 -2
- package/dist/protocol/dist/board-schema.js +7 -22
- package/package.json +2 -1
|
@@ -1,20 +1,9 @@
|
|
|
1
|
+
import { cardElevation, containRect, drawCardSurface, traceCardOutline } from "../surface.js";
|
|
1
2
|
import { positionShell } from "./base-card-renderer.js";
|
|
2
3
|
import { drawFarPlate } from "./far-plate.js";
|
|
3
4
|
import { Container, Graphics, Rectangle, Sprite, Texture } from "pixi.js";
|
|
4
5
|
//#region src/board/render/renderers/image-card-renderer.ts
|
|
5
6
|
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
6
|
-
const RADIUS = 2;
|
|
7
|
-
function layoutContain(sprite, width, height) {
|
|
8
|
-
const texture = sprite.texture;
|
|
9
|
-
const tw = texture.width;
|
|
10
|
-
const th = texture.height;
|
|
11
|
-
if (!tw || !th) return;
|
|
12
|
-
const scale = Math.min(width / tw, height / th);
|
|
13
|
-
sprite.width = tw * scale;
|
|
14
|
-
sprite.height = th * scale;
|
|
15
|
-
sprite.x = (width - sprite.width) / 2;
|
|
16
|
-
sprite.y = (height - sprite.height) / 2;
|
|
17
|
-
}
|
|
18
7
|
function cropRect(crop) {
|
|
19
8
|
if (!crop) return null;
|
|
20
9
|
const w = Math.min(crop.w, 1 - crop.x);
|
|
@@ -48,70 +37,85 @@ function showTexture(parts, texture, crop) {
|
|
|
48
37
|
if (parts.sprite.texture !== display) parts.sprite.texture = display;
|
|
49
38
|
if (previous && previous !== parts.cropped) previous.destroy(false);
|
|
50
39
|
}
|
|
40
|
+
const PRINT_MARGIN = 6;
|
|
51
41
|
function sync(container, item, context) {
|
|
52
42
|
const parts = partsByContainer.get(container);
|
|
53
43
|
if (!parts) return;
|
|
54
44
|
positionShell(parts.root, item);
|
|
55
45
|
const { width, height } = item.frame;
|
|
56
|
-
const selected = context.selectedIds.has(item.id);
|
|
57
|
-
const hovered = context.hoveredId === item.id;
|
|
58
46
|
const key = context.assetKey(item);
|
|
59
47
|
const texture = key ? context.getTexture(key) : null;
|
|
60
48
|
const failed = Boolean(key && !texture && context.hasError(key));
|
|
61
|
-
const
|
|
49
|
+
const elevation = cardElevation(context, item.id);
|
|
62
50
|
const sig = [
|
|
63
51
|
key ?? "",
|
|
64
52
|
width,
|
|
65
53
|
height,
|
|
66
|
-
|
|
67
|
-
hovered,
|
|
68
|
-
texId,
|
|
54
|
+
texture ? `${texture.width}x${texture.height}` : "none",
|
|
69
55
|
JSON.stringify(item.props.crop ?? null),
|
|
70
56
|
failed,
|
|
71
|
-
|
|
57
|
+
elevation,
|
|
58
|
+
context.themeKey
|
|
72
59
|
].join("|");
|
|
73
60
|
if (sig === parts.sig) return;
|
|
74
61
|
parts.sig = sig;
|
|
75
62
|
showTexture(parts, texture, item.props.crop);
|
|
63
|
+
const display = parts.sprite.texture;
|
|
64
|
+
const framed = context.style.media === "framed";
|
|
65
|
+
const margin = framed ? Math.min(PRINT_MARGIN, width / 8, height / 8) : 0;
|
|
66
|
+
const area = {
|
|
67
|
+
x: margin,
|
|
68
|
+
y: margin,
|
|
69
|
+
width: width - margin * 2,
|
|
70
|
+
height: height - margin * 2
|
|
71
|
+
};
|
|
72
|
+
const fitted = texture ? containRect(area.width, area.height, display.width, display.height) : {
|
|
73
|
+
...area,
|
|
74
|
+
x: 0,
|
|
75
|
+
y: 0
|
|
76
|
+
};
|
|
77
|
+
const picture = {
|
|
78
|
+
x: area.x + fitted.x,
|
|
79
|
+
y: area.y + fitted.y,
|
|
80
|
+
width: fitted.width,
|
|
81
|
+
height: fitted.height
|
|
82
|
+
};
|
|
83
|
+
const card = framed ? {
|
|
84
|
+
x: picture.x - margin,
|
|
85
|
+
y: picture.y - margin,
|
|
86
|
+
width: picture.width + margin * 2,
|
|
87
|
+
height: picture.height + margin * 2
|
|
88
|
+
} : picture;
|
|
76
89
|
parts.sprite.visible = Boolean(texture);
|
|
77
|
-
if (texture)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
parts.
|
|
81
|
-
color: context.palette.surface,
|
|
82
|
-
alpha: .85
|
|
83
|
-
}).roundRect(0, 0, width, height, RADIUS).stroke({
|
|
84
|
-
color: failed ? context.palette.border : context.palette.hover,
|
|
85
|
-
width: 1,
|
|
86
|
-
alpha: .9
|
|
87
|
-
});
|
|
88
|
-
const cx = width / 2;
|
|
89
|
-
const cy = height / 2;
|
|
90
|
-
if (failed) parts.placeholder.moveTo(cx - 10, cy - 10).lineTo(cx + 10, cy + 10).moveTo(cx + 10, cy - 10).lineTo(cx - 10, cy + 10).stroke({
|
|
91
|
-
color: context.palette.muted,
|
|
92
|
-
width: 1.5,
|
|
93
|
-
alpha: .8
|
|
94
|
-
});
|
|
95
|
-
else parts.placeholder.circle(cx, cy, 6).stroke({
|
|
96
|
-
color: context.palette.muted,
|
|
97
|
-
width: 1.25,
|
|
98
|
-
alpha: .55
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
parts.placeholder.visible = !texture;
|
|
102
|
-
if (selected || hovered) {
|
|
103
|
-
const hasPixels = Boolean(texture) && parts.sprite.width > 0;
|
|
104
|
-
const ox = hasPixels ? parts.sprite.x : 0;
|
|
105
|
-
const oy = hasPixels ? parts.sprite.y : 0;
|
|
106
|
-
const ow = hasPixels ? parts.sprite.width : width;
|
|
107
|
-
const oh = hasPixels ? parts.sprite.height : height;
|
|
108
|
-
parts.placeholder.visible = true;
|
|
109
|
-
parts.placeholder.roundRect(ox, oy, ow, oh, RADIUS).stroke({
|
|
110
|
-
color: selected ? context.palette.brand : context.palette.muted,
|
|
111
|
-
width: selected ? 2 : 1,
|
|
112
|
-
alpha: selected ? .95 : .7
|
|
113
|
-
});
|
|
90
|
+
if (texture) {
|
|
91
|
+
parts.sprite.position.set(picture.x, picture.y);
|
|
92
|
+
parts.sprite.width = picture.width;
|
|
93
|
+
parts.sprite.height = picture.height;
|
|
114
94
|
}
|
|
95
|
+
traceCardOutline(parts.mask.clear(), picture, framed ? {
|
|
96
|
+
corner: "round",
|
|
97
|
+
radius: 0
|
|
98
|
+
} : context.style).fill({ color: 16777215 });
|
|
99
|
+
parts.placeholder.clear();
|
|
100
|
+
if (texture && !framed) return;
|
|
101
|
+
drawCardSurface(parts.placeholder, card, context.style, {
|
|
102
|
+
fill: context.palette.surface,
|
|
103
|
+
border: texture ? null : failed ? context.palette.border : context.palette.hover,
|
|
104
|
+
elevation
|
|
105
|
+
});
|
|
106
|
+
if (texture) return;
|
|
107
|
+
const cx = width / 2;
|
|
108
|
+
const cy = height / 2;
|
|
109
|
+
if (failed) parts.placeholder.moveTo(cx - 10, cy - 10).lineTo(cx + 10, cy + 10).moveTo(cx + 10, cy - 10).lineTo(cx - 10, cy + 10).stroke({
|
|
110
|
+
color: context.palette.muted,
|
|
111
|
+
width: 1.5,
|
|
112
|
+
alpha: .8
|
|
113
|
+
});
|
|
114
|
+
else parts.placeholder.circle(cx, cy, 6).stroke({
|
|
115
|
+
color: context.palette.muted,
|
|
116
|
+
width: 1.25,
|
|
117
|
+
alpha: .55
|
|
118
|
+
});
|
|
115
119
|
}
|
|
116
120
|
const imageCardRenderer = {
|
|
117
121
|
id: "image-card",
|
|
@@ -121,17 +125,13 @@ const imageCardRenderer = {
|
|
|
121
125
|
const sprite = new Sprite(Texture.EMPTY);
|
|
122
126
|
const placeholder = new Graphics();
|
|
123
127
|
const mask = new Graphics();
|
|
124
|
-
const maskBox = () => {
|
|
125
|
-
mask.clear();
|
|
126
|
-
if (item.type === "image") mask.roundRect(0, 0, item.frame.width, item.frame.height, RADIUS).fill({ color: 16777215 });
|
|
127
|
-
};
|
|
128
|
-
maskBox();
|
|
129
128
|
root.addChild(placeholder, sprite, mask);
|
|
130
129
|
sprite.mask = mask;
|
|
131
130
|
partsByContainer.set(root, {
|
|
132
131
|
root,
|
|
133
132
|
sprite,
|
|
134
133
|
placeholder,
|
|
134
|
+
mask,
|
|
135
135
|
sig: "",
|
|
136
136
|
cropped: null,
|
|
137
137
|
cropSource: null,
|
|
@@ -141,12 +141,7 @@ const imageCardRenderer = {
|
|
|
141
141
|
return root;
|
|
142
142
|
},
|
|
143
143
|
update: (container, item, context) => {
|
|
144
|
-
if (item.type
|
|
145
|
-
const parts = partsByContainer.get(container);
|
|
146
|
-
if (!parts) return;
|
|
147
|
-
const mask = parts.root.children.find((child) => child !== parts.sprite && child !== parts.placeholder);
|
|
148
|
-
if (mask) mask.clear().roundRect(0, 0, item.frame.width, item.frame.height, RADIUS).fill({ color: 16777215 });
|
|
149
|
-
sync(container, item, context);
|
|
144
|
+
if (item.type === "image") sync(container, item, context);
|
|
150
145
|
},
|
|
151
146
|
renderFar: (graphics, item, context) => {
|
|
152
147
|
drawFarPlate(graphics, item.frame, {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { BoardRenderContext } from "./board-renderer-registry.js";
|
|
2
|
+
import { Graphics } from "pixi.js";
|
|
3
|
+
//#region src/board/render/renderers/media-key.d.ts
|
|
4
|
+
declare function drawMediaPlayKey(graphics: Graphics, cx: number, cy: number, radius: number, context: BoardRenderContext, solid?: boolean): void;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { drawMediaPlayKey };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { tracePixelRect } from "../surface.js";
|
|
2
|
+
//#region src/board/render/renderers/media-key.ts
|
|
3
|
+
const MEDIA_SCRIM = 0;
|
|
4
|
+
const MEDIA_INK = 16777215;
|
|
5
|
+
function drawMediaPlayKey(graphics, cx, cy, radius, context, solid = false) {
|
|
6
|
+
const face = solid ? context.palette.text : MEDIA_SCRIM;
|
|
7
|
+
const ink = solid ? context.palette.surface : MEDIA_INK;
|
|
8
|
+
if (context.style.corner === "pixel") {
|
|
9
|
+
const size = Math.round(radius * 1.6);
|
|
10
|
+
tracePixelRect(graphics, Math.round(cx - size / 2), Math.round(cy - size / 2), size, size, 2).fill({
|
|
11
|
+
color: face,
|
|
12
|
+
alpha: solid ? 1 : .62
|
|
13
|
+
});
|
|
14
|
+
const cell = Math.max(1, Math.round(size / 14));
|
|
15
|
+
const x = Math.round(cx - cell * 1.5);
|
|
16
|
+
const y = Math.round(cy - cell * 3.5);
|
|
17
|
+
for (let row = 0; row < 7; row += 1) graphics.rect(x, y + row * cell, (4 - Math.abs(row - 3)) * cell, cell);
|
|
18
|
+
graphics.fill({ color: ink });
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
graphics.circle(cx, cy, radius).fill({
|
|
22
|
+
color: face,
|
|
23
|
+
alpha: solid ? 1 : .45
|
|
24
|
+
});
|
|
25
|
+
const size = radius * .5;
|
|
26
|
+
graphics.poly([
|
|
27
|
+
cx - size * .45,
|
|
28
|
+
cy - size * .75,
|
|
29
|
+
cx + size * .8,
|
|
30
|
+
cy,
|
|
31
|
+
cx - size * .45,
|
|
32
|
+
cy + size * .75
|
|
33
|
+
]).fill({
|
|
34
|
+
color: ink,
|
|
35
|
+
alpha: .96
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { drawMediaPlayKey };
|
|
@@ -2,9 +2,9 @@ import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
|
|
|
2
2
|
import { worldPoint } from "../../model/geometry.js";
|
|
3
3
|
import { itemAlpha, itemColor } from "../palette.js";
|
|
4
4
|
import { syncTextResolution, syncTextWrapWidth, textResolutionForZoom } from "../text-resolution.js";
|
|
5
|
+
import { dashPattern, roundRectPoints, tracePolyline, trimPolyline } from "../stroke.js";
|
|
5
6
|
import { positionShell } from "./base-card-renderer.js";
|
|
6
7
|
import { drawFarPlate } from "./far-plate.js";
|
|
7
|
-
import { dashPattern, roundRectPoints, tracePolyline, trimPolyline } from "../stroke.js";
|
|
8
8
|
import { Container, Graphics, GraphicsPath, Matrix, Text } from "pixi.js";
|
|
9
9
|
//#region src/board/render/renderers/shape-card-renderer.ts
|
|
10
10
|
const DEFAULT_ROUNDED_RADIUS = 8;
|
|
@@ -95,8 +95,6 @@ function sync(container, item, context) {
|
|
|
95
95
|
positionShell(parts.root, item);
|
|
96
96
|
const { width, height } = item.frame;
|
|
97
97
|
const { props, style } = item;
|
|
98
|
-
const selected = context.selectedIds.has(item.id);
|
|
99
|
-
const hovered = context.hoveredId === item.id;
|
|
100
98
|
const stroke = itemColor(context, style.stroke ?? style.fill, "brand");
|
|
101
99
|
const fill = itemColor(context, style.fill ?? style.stroke, "brand", "fill");
|
|
102
100
|
const fillOpacity = (style.fillOpacity ?? (style.fill ? 1 : 0)) * itemAlpha(context, style.fill ?? style.stroke);
|
|
@@ -106,8 +104,6 @@ function sync(container, item, context) {
|
|
|
106
104
|
const visualSig = [
|
|
107
105
|
width,
|
|
108
106
|
height,
|
|
109
|
-
selected,
|
|
110
|
-
hovered,
|
|
111
107
|
props.geometry,
|
|
112
108
|
props.path,
|
|
113
109
|
fill,
|
|
@@ -117,8 +113,7 @@ function sync(container, item, context) {
|
|
|
117
113
|
strokeWidth,
|
|
118
114
|
style.dash,
|
|
119
115
|
style.radius,
|
|
120
|
-
style.trim
|
|
121
|
-
context.palette.muted
|
|
116
|
+
style.trim
|
|
122
117
|
].join("|");
|
|
123
118
|
if (visualSig !== parts.visualSig) {
|
|
124
119
|
parts.visualSig = visualSig;
|
|
@@ -130,8 +125,7 @@ function sync(container, item, context) {
|
|
|
130
125
|
alpha: fillOpacity
|
|
131
126
|
});
|
|
132
127
|
}
|
|
133
|
-
const
|
|
134
|
-
const pattern = dashPattern(style.dash, lineWidth);
|
|
128
|
+
const pattern = dashPattern(style.dash, strokeWidth);
|
|
135
129
|
const partial = style.trim !== void 0 && style.trim < 1;
|
|
136
130
|
if (strokeWidth > 0 && (!partial || style.trim)) {
|
|
137
131
|
if ((pattern || partial) && props.geometry !== "path") {
|
|
@@ -139,9 +133,9 @@ function sync(container, item, context) {
|
|
|
139
133
|
tracePolyline(parts.shape, trimPolyline(outlinePoints(props.geometry, width, height, radius), style.trim), pattern);
|
|
140
134
|
} else traceShapeOutline(parts.shape, props, width, height, style.radius);
|
|
141
135
|
parts.shape.stroke({
|
|
142
|
-
color:
|
|
143
|
-
width:
|
|
144
|
-
alpha:
|
|
136
|
+
color: stroke,
|
|
137
|
+
width: strokeWidth,
|
|
138
|
+
alpha: .85 * strokeAlpha,
|
|
145
139
|
cap: partial || pattern ? "round" : "butt",
|
|
146
140
|
join: "round"
|
|
147
141
|
});
|
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
import { BoardCardRenderer } from "./board-renderer-registry.js";
|
|
2
2
|
//#region src/board/render/renderers/task-card-renderer.d.ts
|
|
3
|
-
declare const TASK_CARD_FULL_DETAIL_ZOOM = 0.55;
|
|
4
|
-
declare function formatTaskElapsed(ms: number): string;
|
|
5
|
-
declare function containTaskPreviewRect(width: number, height: number, imageWidth: number, imageHeight: number): {
|
|
6
|
-
x: number;
|
|
7
|
-
y: number;
|
|
8
|
-
width: number;
|
|
9
|
-
height: number;
|
|
10
|
-
};
|
|
11
3
|
declare const taskCardRenderer: BoardCardRenderer;
|
|
12
4
|
//#endregion
|
|
13
|
-
export {
|
|
5
|
+
export { taskCardRenderer };
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { BOARD_FONT_STACK, BOARD_MONO_FONT_STACK } from "../../../protocol/dist/board-constants.js";
|
|
2
2
|
import { featuredTaskArtifact } from "../../../protocol/dist/board-task.js";
|
|
3
|
+
import { fitTextToLines } from "../text-fit.js";
|
|
4
|
+
import { boardCardDetailed, syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
|
|
5
|
+
import { createCardTag, formatBoardDuration, syncCardTag, zoomCardTag } from "../card-tag.js";
|
|
3
6
|
import { drawAudioWaveform } from "../audio-waveform.js";
|
|
4
|
-
import {
|
|
7
|
+
import { cardElevation, containRect, drawCardSurface, traceCardOutline } from "../surface.js";
|
|
5
8
|
import { positionShell } from "./base-card-renderer.js";
|
|
6
9
|
import { drawFarPlate } from "./far-plate.js";
|
|
7
|
-
import { fitTextToLines } from "./file-card-renderer.js";
|
|
8
10
|
import { Container, FillGradient, Graphics, Sprite, Text } from "pixi.js";
|
|
9
11
|
//#region src/board/render/renderers/task-card-renderer.ts
|
|
10
|
-
const RADIUS = 4;
|
|
11
12
|
const PADDING = 12;
|
|
12
13
|
const META_HEIGHT = 28;
|
|
13
|
-
const TASK_CARD_FULL_DETAIL_ZOOM = .55;
|
|
14
14
|
const PLAY_BADGE_RADIUS = 15;
|
|
15
15
|
const SHIMMER_PERIOD_MS = 1600;
|
|
16
16
|
const SHIMMER_FRAME_MS = 33;
|
|
@@ -62,30 +62,6 @@ function statusColor(status, context) {
|
|
|
62
62
|
if (status === "running") return context.colors.brand.stroke;
|
|
63
63
|
return context.colors.neutral.stroke;
|
|
64
64
|
}
|
|
65
|
-
function formatTaskElapsed(ms) {
|
|
66
|
-
const total = Math.max(0, Math.floor(ms / SECOND));
|
|
67
|
-
const hours = Math.floor(total / 3600);
|
|
68
|
-
const minutes = Math.floor(total % 3600 / 60);
|
|
69
|
-
const seconds = String(total % 60).padStart(2, "0");
|
|
70
|
-
return hours > 0 ? `${hours}:${String(minutes).padStart(2, "0")}:${seconds}` : `${minutes}:${seconds}`;
|
|
71
|
-
}
|
|
72
|
-
function containTaskPreviewRect(width, height, imageWidth, imageHeight) {
|
|
73
|
-
if (width <= 0 || height <= 0 || imageWidth <= 0 || imageHeight <= 0) return {
|
|
74
|
-
x: 0,
|
|
75
|
-
y: 0,
|
|
76
|
-
width: 0,
|
|
77
|
-
height: 0
|
|
78
|
-
};
|
|
79
|
-
const scale = Math.min(width / imageWidth, height / imageHeight);
|
|
80
|
-
const renderedWidth = imageWidth * scale;
|
|
81
|
-
const renderedHeight = imageHeight * scale;
|
|
82
|
-
return {
|
|
83
|
-
x: (width - renderedWidth) / 2,
|
|
84
|
-
y: (height - renderedHeight) / 2,
|
|
85
|
-
width: renderedWidth,
|
|
86
|
-
height: renderedHeight
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
65
|
function drawPlayBadge(graphics, rect, context) {
|
|
90
66
|
const radius = Math.min(PLAY_BADGE_RADIUS, Math.max(8, Math.min(rect.width, rect.height) * .16));
|
|
91
67
|
const cx = rect.x + rect.width / 2;
|
|
@@ -208,17 +184,46 @@ function drawShimmerBand(graphics, width, height, color) {
|
|
|
208
184
|
const band = Math.max(48, width * .45);
|
|
209
185
|
graphics.clear().rect(-band, 0, band, height).fill(shimmerGradient(color));
|
|
210
186
|
}
|
|
187
|
+
const MOSAIC_FRAME_MS = 160;
|
|
188
|
+
const MOSAIC_CELL = 8;
|
|
189
|
+
function drawMosaic(graphics, width, height, color, frame) {
|
|
190
|
+
const columns = Math.max(1, Math.floor(width / MOSAIC_CELL));
|
|
191
|
+
const rows = Math.max(1, Math.floor(height / MOSAIC_CELL));
|
|
192
|
+
const left = (width - columns * MOSAIC_CELL) / 2;
|
|
193
|
+
const top = (height - rows * MOSAIC_CELL) / 2;
|
|
194
|
+
graphics.clear();
|
|
195
|
+
for (const [threshold, alpha] of [[.08, .16], [.02, .3]]) {
|
|
196
|
+
for (let row = 0; row < rows; row += 1) for (let column = 0; column < columns; column += 1) {
|
|
197
|
+
let hash = Math.imul(column + 1, 2654435761) ^ Math.imul(row + 1, 2246822507) ^ Math.imul(frame + 1, 3266489909);
|
|
198
|
+
hash ^= hash >>> 15;
|
|
199
|
+
hash = Math.imul(hash, 739982445);
|
|
200
|
+
hash ^= hash >>> 12;
|
|
201
|
+
if ((hash >>> 0) / 4294967295 < threshold) graphics.rect(left + column * MOSAIC_CELL + 1, top + row * MOSAIC_CELL + 1, 6, 6);
|
|
202
|
+
}
|
|
203
|
+
graphics.fill({
|
|
204
|
+
color,
|
|
205
|
+
alpha
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
211
209
|
function advance(parts, clock) {
|
|
212
210
|
const { motion } = parts;
|
|
213
211
|
let next = null;
|
|
214
|
-
if (motion.
|
|
212
|
+
if (motion.mosaic) {
|
|
213
|
+
const frame = Math.floor(clock / MOSAIC_FRAME_MS);
|
|
214
|
+
if (frame !== motion.mosaic.step) {
|
|
215
|
+
motion.mosaic.step = frame;
|
|
216
|
+
drawMosaic(parts.shimmer, motion.width, motion.mosaic.height, motion.mosaic.color, frame);
|
|
217
|
+
}
|
|
218
|
+
next = MOSAIC_FRAME_MS - clock % MOSAIC_FRAME_MS;
|
|
219
|
+
} else if (motion.shimmer) {
|
|
215
220
|
const travel = motion.width + parts.shimmer.width;
|
|
216
221
|
parts.shimmer.x = clock % SHIMMER_PERIOD_MS / SHIMMER_PERIOD_MS * travel;
|
|
217
222
|
next = SHIMMER_FRAME_MS;
|
|
218
223
|
}
|
|
219
224
|
if (motion.startedAt !== null) {
|
|
220
225
|
const elapsedMs = Date.now() - motion.startedAt;
|
|
221
|
-
const elapsed =
|
|
226
|
+
const elapsed = formatBoardDuration(elapsedMs);
|
|
222
227
|
if (elapsed !== motion.elapsed) {
|
|
223
228
|
motion.elapsed = elapsed;
|
|
224
229
|
parts.body.text = `${motion.label} · ${elapsed}`;
|
|
@@ -241,9 +246,13 @@ function sync(container, item, context) {
|
|
|
241
246
|
const { width, height } = item.frame;
|
|
242
247
|
const { snapshot } = item.props;
|
|
243
248
|
const { status, startedAt } = liveStatus(item, context);
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
|
|
249
|
+
const full = boardCardDetailed(context.zoom);
|
|
250
|
+
const elevation = cardElevation(context, item.id);
|
|
251
|
+
syncCardTag(parts.tag, {
|
|
252
|
+
glyph: "task",
|
|
253
|
+
text: snapshot.title,
|
|
254
|
+
width
|
|
255
|
+
}, context);
|
|
247
256
|
const working = status === "pending" || status === "running";
|
|
248
257
|
const color = statusColor(status, context);
|
|
249
258
|
const key = context.assetKey(item);
|
|
@@ -270,7 +279,7 @@ function sync(container, item, context) {
|
|
|
270
279
|
const showBadge = full && Boolean(count);
|
|
271
280
|
const label = stateLabel(surface, context);
|
|
272
281
|
const counting = full && working && startedAt !== null && surface !== null;
|
|
273
|
-
const elapsed = counting ?
|
|
282
|
+
const elapsed = counting ? formatBoardDuration(Date.now() - startedAt) : "";
|
|
274
283
|
const detailText = !full || !surface ? "" : status === "failed" ? snapshot.errorExcerpt ?? "" : working ? snapshot.promptExcerpt ?? "" : "";
|
|
275
284
|
const textArtifact = kind === "text" && artifact?.type === "text";
|
|
276
285
|
const bodyText = textArtifact ? artifact.textExcerpt : counting ? `${label} · ${elapsed}` : label;
|
|
@@ -290,10 +299,13 @@ function sync(container, item, context) {
|
|
|
290
299
|
height,
|
|
291
300
|
color,
|
|
292
301
|
context.palette.text,
|
|
293
|
-
context.palette.muted
|
|
302
|
+
context.palette.muted,
|
|
303
|
+
context.style.font
|
|
294
304
|
].join("|");
|
|
295
305
|
if (textSig !== parts.textSig) {
|
|
296
306
|
parts.textSig = textSig;
|
|
307
|
+
parts.body.style.fontFamily = context.style.font;
|
|
308
|
+
parts.detail.style.fontFamily = context.style.font;
|
|
297
309
|
const wrap = Math.max(24, width - 24);
|
|
298
310
|
parts.body.style.fill = surface === "failed" ? color : context.palette.text;
|
|
299
311
|
parts.body.anchor.set(textArtifact ? 0 : .5, 0);
|
|
@@ -309,9 +321,8 @@ function sync(container, item, context) {
|
|
|
309
321
|
const visualSig = [
|
|
310
322
|
width,
|
|
311
323
|
height,
|
|
312
|
-
selected,
|
|
313
|
-
hovered,
|
|
314
324
|
full,
|
|
325
|
+
elevation,
|
|
315
326
|
color,
|
|
316
327
|
kind,
|
|
317
328
|
surface,
|
|
@@ -322,30 +333,30 @@ function sync(container, item, context) {
|
|
|
322
333
|
artifact?.id ?? "none",
|
|
323
334
|
item.props.taskRunId,
|
|
324
335
|
texture ? `${texture.width}x${texture.height}` : "none",
|
|
325
|
-
context.
|
|
326
|
-
context.palette.hover,
|
|
327
|
-
context.palette.border,
|
|
328
|
-
context.palette.bg,
|
|
329
|
-
context.palette.text
|
|
336
|
+
context.themeKey
|
|
330
337
|
].join("|");
|
|
331
338
|
if (visualSig !== parts.visualSig) {
|
|
332
339
|
parts.visualSig = visualSig;
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
+
const rect = {
|
|
341
|
+
x: 0,
|
|
342
|
+
y: 0,
|
|
343
|
+
width,
|
|
344
|
+
height
|
|
345
|
+
};
|
|
346
|
+
drawCardSurface(parts.plate.clear(), rect, context.style, {
|
|
347
|
+
fill: context.palette.surface,
|
|
348
|
+
border: context.palette.border,
|
|
349
|
+
borderAlpha: .82,
|
|
350
|
+
elevation
|
|
340
351
|
});
|
|
341
|
-
parts.clip.clear()
|
|
352
|
+
traceCardOutline(parts.clip.clear(), rect, context.style, -1).fill({ color: 16777215 });
|
|
342
353
|
parts.previewBg.clear().rect(frame.x, frame.y, frame.width, frame.height).fill({
|
|
343
354
|
color: context.palette.hover,
|
|
344
355
|
alpha: .5
|
|
345
356
|
});
|
|
346
357
|
parts.previewMask.clear().rect(frame.x, frame.y, frame.width, frame.height).fill({ color: 16777215 });
|
|
347
|
-
drawShimmerBand(parts.shimmer, frame.width, frame.height, context.palette.text);
|
|
348
|
-
parts.shimmer.
|
|
358
|
+
if (context.style.progress === "shimmer") drawShimmerBand(parts.shimmer, frame.width, frame.height, context.palette.text);
|
|
359
|
+
parts.shimmer.position.set(context.style.progress === "shimmer" ? 0 : frame.x, frame.y);
|
|
349
360
|
parts.previewArt.clear();
|
|
350
361
|
if (kind === "waveform") {
|
|
351
362
|
const bottomInset = showMeta ? META_HEIGHT : 0;
|
|
@@ -384,7 +395,7 @@ function sync(container, item, context) {
|
|
|
384
395
|
parts.preview.visible = showPreview;
|
|
385
396
|
if (showPreview && texture) {
|
|
386
397
|
if (parts.preview.texture !== texture) parts.preview.texture = texture;
|
|
387
|
-
const fitted =
|
|
398
|
+
const fitted = containRect(frame.width, frame.height, texture.width, texture.height);
|
|
388
399
|
parts.preview.position.set(frame.x + fitted.x, frame.y + fitted.y);
|
|
389
400
|
parts.preview.width = fitted.width;
|
|
390
401
|
parts.preview.height = fitted.height;
|
|
@@ -400,6 +411,12 @@ function sync(container, item, context) {
|
|
|
400
411
|
parts.badge.position.set(width - 8 - badgeWidth / 2, 18);
|
|
401
412
|
parts.motion.width = frame.width;
|
|
402
413
|
parts.motion.shimmer = full && working && kind !== "texture" && !context.reducedMotion;
|
|
414
|
+
const mosaic = parts.motion.shimmer && context.style.progress === "mosaic";
|
|
415
|
+
parts.motion.mosaic = mosaic ? {
|
|
416
|
+
color: context.palette.brand,
|
|
417
|
+
height: frame.height,
|
|
418
|
+
step: -1
|
|
419
|
+
} : null;
|
|
403
420
|
parts.shimmer.visible = parts.motion.shimmer;
|
|
404
421
|
parts.motion.startedAt = counting ? startedAt : null;
|
|
405
422
|
parts.motion.label = label;
|
|
@@ -439,10 +456,14 @@ const taskCardRenderer = {
|
|
|
439
456
|
detail.anchor.set(.5, 0);
|
|
440
457
|
detail.style.align = "center";
|
|
441
458
|
preview.mask = previewMask;
|
|
442
|
-
|
|
443
|
-
|
|
459
|
+
const content = new Container();
|
|
460
|
+
content.mask = clip;
|
|
461
|
+
content.addChild(previewBg, shimmer, preview, previewMask, previewArt, body, detail, meta, badge);
|
|
462
|
+
const tag = createCardTag();
|
|
463
|
+
root.addChild(plate, content, clip, tag.root);
|
|
444
464
|
partsByContainer.set(root, {
|
|
445
465
|
root,
|
|
466
|
+
tag,
|
|
446
467
|
plate,
|
|
447
468
|
clip,
|
|
448
469
|
previewBg,
|
|
@@ -462,6 +483,7 @@ const taskCardRenderer = {
|
|
|
462
483
|
itemId: "",
|
|
463
484
|
width: 0,
|
|
464
485
|
shimmer: false,
|
|
486
|
+
mosaic: null,
|
|
465
487
|
startedAt: null,
|
|
466
488
|
label: "",
|
|
467
489
|
elapsed: "",
|
|
@@ -479,6 +501,10 @@ const taskCardRenderer = {
|
|
|
479
501
|
const parts = partsByContainer.get(container);
|
|
480
502
|
return parts ? advance(parts, clock) : null;
|
|
481
503
|
},
|
|
504
|
+
zoom: (container, zoom) => {
|
|
505
|
+
const tag = partsByContainer.get(container)?.tag;
|
|
506
|
+
if (tag) zoomCardTag(tag, zoom);
|
|
507
|
+
},
|
|
482
508
|
renderFar: (graphics, item, context) => {
|
|
483
509
|
if (item.type !== "task") return;
|
|
484
510
|
const { status } = liveStatus(item, context);
|
|
@@ -500,4 +526,4 @@ const taskCardRenderer = {
|
|
|
500
526
|
}
|
|
501
527
|
};
|
|
502
528
|
//#endregion
|
|
503
|
-
export {
|
|
529
|
+
export { taskCardRenderer };
|
|
@@ -1,40 +1,41 @@
|
|
|
1
1
|
import { BOARD_MONO_FONT_STACK } from "../../../protocol/dist/board-constants.js";
|
|
2
2
|
import { syncTextResolution, textResolutionForZoom } from "../text-resolution.js";
|
|
3
|
+
import { cardElevation, drawCardSurface, traceCardOutline } from "../surface.js";
|
|
3
4
|
import { positionShell } from "./base-card-renderer.js";
|
|
4
5
|
import { drawFarPlate } from "./far-plate.js";
|
|
5
6
|
import { Container, Graphics, Text } from "pixi.js";
|
|
6
7
|
//#region src/board/render/renderers/unknown-card-renderer.ts
|
|
7
|
-
const RADIUS = 10;
|
|
8
8
|
const partsByContainer = /* @__PURE__ */ new WeakMap();
|
|
9
9
|
function sync(container, item, context) {
|
|
10
10
|
const parts = partsByContainer.get(container);
|
|
11
11
|
if (!parts) return;
|
|
12
12
|
positionShell(parts.root, item);
|
|
13
13
|
const { width, height } = item.frame;
|
|
14
|
-
const selected = context.selectedIds.has(item.id);
|
|
15
14
|
const realType = item.type;
|
|
16
15
|
syncTextResolution(parts.label, parts, context.zoom);
|
|
16
|
+
const elevation = cardElevation(context, item.id);
|
|
17
17
|
const visualSig = [
|
|
18
18
|
width,
|
|
19
19
|
height,
|
|
20
|
-
|
|
21
|
-
context.
|
|
22
|
-
context.palette.brand,
|
|
23
|
-
context.palette.border,
|
|
24
|
-
context.palette.muted
|
|
20
|
+
elevation,
|
|
21
|
+
context.themeKey
|
|
25
22
|
].join("|");
|
|
26
23
|
if (visualSig !== parts.visualSig) {
|
|
27
24
|
parts.visualSig = visualSig;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
25
|
+
const rect = {
|
|
26
|
+
x: 0,
|
|
27
|
+
y: 0,
|
|
28
|
+
width,
|
|
29
|
+
height
|
|
30
|
+
};
|
|
31
|
+
drawCardSurface(parts.box.clear(), rect, context.style, {
|
|
32
|
+
fill: context.palette.surface,
|
|
33
|
+
fillAlpha: .7,
|
|
34
|
+
border: context.palette.border,
|
|
35
|
+
borderAlpha: .8,
|
|
36
|
+
elevation
|
|
36
37
|
});
|
|
37
|
-
parts.box
|
|
38
|
+
traceCardOutline(parts.box, rect, context.style, -6).stroke({
|
|
38
39
|
color: context.palette.muted,
|
|
39
40
|
width: 1,
|
|
40
41
|
alpha: .4
|