@neta-art/cohub 11.1.0 → 11.2.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.
Files changed (67) hide show
  1. package/dist/board/editor/context.d.ts +1 -0
  2. package/dist/board/editor/context.js +1 -0
  3. package/dist/board/editor/editor.d.ts +1 -0
  4. package/dist/board/editor/editor.js +7 -0
  5. package/dist/board/editor/interaction.d.ts +4 -0
  6. package/dist/board/editor/interaction.js +16 -7
  7. package/dist/board/editor/selection-transform.d.ts +1 -1
  8. package/dist/board/editor/selection-transform.js +13 -2
  9. package/dist/board/export/index.d.ts +2 -0
  10. package/dist/board/export/index.js +1 -0
  11. package/dist/board/export/scene.d.ts +2 -0
  12. package/dist/board/export/scene.js +4 -0
  13. package/dist/board/model/file-preview.d.ts +0 -1
  14. package/dist/board/model/file-preview.js +10 -1
  15. package/dist/board/render/audio-waveform.d.ts +1 -1
  16. package/dist/board/render/audio-waveform.js +8 -5
  17. package/dist/board/render/card-tag.d.ts +24 -0
  18. package/dist/board/render/card-tag.js +205 -0
  19. package/dist/board/render/index.d.ts +7 -4
  20. package/dist/board/render/index.js +7 -4
  21. package/dist/board/render/media-interaction.d.ts +2 -1
  22. package/dist/board/render/media-interaction.js +14 -6
  23. package/dist/board/render/palette.js +2 -2
  24. package/dist/board/render/renderers/arrow-card-renderer.js +20 -16
  25. package/dist/board/render/renderers/audio-card-renderer.d.ts +8 -1
  26. package/dist/board/render/renderers/audio-card-renderer.js +64 -60
  27. package/dist/board/render/renderers/base-card-renderer.d.ts +1 -26
  28. package/dist/board/render/renderers/base-card-renderer.js +6 -92
  29. package/dist/board/render/renderers/board-renderer-registry.d.ts +8 -0
  30. package/dist/board/render/renderers/board-renderer-registry.js +3 -3
  31. package/dist/board/render/renderers/custom-card-renderer.js +4 -7
  32. package/dist/board/render/renderers/effect-card-renderer.js +0 -5
  33. package/dist/board/render/renderers/file-card-renderer.d.ts +1 -11
  34. package/dist/board/render/renderers/file-card-renderer.js +43 -105
  35. package/dist/board/render/renderers/frame-card-renderer.js +14 -15
  36. package/dist/board/render/renderers/image-card-renderer.js +62 -67
  37. package/dist/board/render/renderers/media-key.d.ts +6 -0
  38. package/dist/board/render/renderers/media-key.js +39 -0
  39. package/dist/board/render/renderers/shape-card-renderer.js +6 -12
  40. package/dist/board/render/renderers/task-card-renderer.d.ts +1 -9
  41. package/dist/board/render/renderers/task-card-renderer.js +82 -56
  42. package/dist/board/render/renderers/unknown-card-renderer.js +17 -16
  43. package/dist/board/render/renderers/video-card-renderer.js +73 -80
  44. package/dist/board/render/stroke.d.ts +1 -1
  45. package/dist/board/render/stroke.js +5 -4
  46. package/dist/board/render/style.d.ts +29 -0
  47. package/dist/board/render/style.js +54 -0
  48. package/dist/board/render/surface.d.ts +34 -0
  49. package/dist/board/render/surface.js +132 -0
  50. package/dist/board/render/text-fit.d.ts +8 -0
  51. package/dist/board/render/text-fit.js +60 -0
  52. package/dist/board/render/text-resolution.d.ts +2 -1
  53. package/dist/board/render/text-resolution.js +4 -1
  54. package/dist/board/stage/index.d.ts +3 -2
  55. package/dist/board/stage/index.js +2 -1
  56. package/dist/board/stage/overlay.d.ts +41 -0
  57. package/dist/board/stage/overlay.js +182 -0
  58. package/dist/board/stage/scene.d.ts +2 -43
  59. package/dist/board/stage/scene.js +23 -131
  60. package/dist/board/stage/stage.d.ts +2 -0
  61. package/dist/board/stage/stage.js +100 -33
  62. package/dist/chunks/environment.d.ts +997 -3
  63. package/dist/chunks/http.d.ts +48 -1
  64. package/dist/chunks/http.js +8 -0
  65. package/dist/http.d.ts +2 -2
  66. package/dist/index.d.ts +2 -2
  67. package/package.json +2 -1
@@ -1,12 +1,14 @@
1
1
  import { BOARD_FONT_STACK, BOARD_MONO_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
2
  import { fileCategory, fileStem } from "../../model/file-snapshot.js";
3
- import { fileCategoryAccent, filePreviewKind, fileTypeLabel } from "../../model/file-preview.js";
4
- import { syncTextResolution } from "../text-resolution.js";
3
+ import { fileCategoryAccent, fileMetaLine, filePreviewKind, fileTypeLabel } from "../../model/file-preview.js";
4
+ import { fitTextToLines } from "../text-fit.js";
5
+ import { boardCardDetailed, syncTextResolution } from "../text-resolution.js";
6
+ import { createCardTag, syncCardTag, zoomCardTag } from "../card-tag.js";
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 { CanvasTextMetrics, Container, Graphics, Sprite, Text, Texture } from "pixi.js";
10
+ import { Container, Graphics, Sprite, Text, Texture } from "pixi.js";
8
11
  //#region src/board/render/renderers/file-card-renderer.ts
9
- const RADIUS = 4;
10
12
  const PADDING = 10;
11
13
  const COVER_RATIO = .56;
12
14
  const MIN_BODY_FOR_COVER = 38;
@@ -19,108 +21,41 @@ const EXCERPT_MAX_LINES = 3;
19
21
  const TYPE_MARK_SIZE = 28;
20
22
  const GAP = 4;
21
23
  const STRIPE = 2;
22
- const LOD_TITLE_ZOOM = .35;
23
- const LOD_BODY_ZOOM = .6;
24
24
  const MAX_DASHES = 40;
25
25
  const partsByContainer = /* @__PURE__ */ new WeakMap();
26
26
  function detailFor(zoom) {
27
- if (zoom < LOD_TITLE_ZOOM) return "plate";
28
- if (zoom < LOD_BODY_ZOOM) return "title";
29
- return "full";
27
+ return boardCardDetailed(zoom) ? "full" : "title";
30
28
  }
31
29
  function coverHeight(item, height) {
32
30
  if (filePreviewKind(item.props.snapshot) !== "cover") return 0;
33
31
  const ideal = Math.round(height * COVER_RATIO);
34
32
  return Math.max(0, Math.min(ideal, height - MIN_BODY_FOR_COVER));
35
33
  }
36
- function ellipsizeWrappedLines(lines, maxLines) {
37
- if (maxLines <= 0 || lines.length === 0) return "";
38
- if (lines.length <= maxLines) return lines.join("\n");
39
- const kept = lines.slice(0, maxLines);
40
- const lastIndex = kept.length - 1;
41
- const last = (kept[lastIndex] ?? "").replace(/\s+$/u, "");
42
- kept[lastIndex] = last ? `${last}…` : "…";
43
- return kept.join("\n");
44
- }
45
- function fitLineWithEllipsis(line, wrapWidth, measureWidth) {
46
- const trimmed = line.replace(/\s+$/u, "");
47
- if (!trimmed) return "…";
48
- const full = `${trimmed}…`;
49
- if (measureWidth(full) <= wrapWidth) return full;
50
- let lo = 0;
51
- let hi = trimmed.length;
52
- let best = "…";
53
- while (lo <= hi) {
54
- const mid = lo + hi >> 1;
55
- const candidate = mid > 0 ? `${trimmed.slice(0, mid)}…` : "…";
56
- if (measureWidth(candidate) <= wrapWidth) {
57
- best = candidate;
58
- lo = mid + 1;
59
- } else hi = mid - 1;
60
- }
61
- return best;
62
- }
63
- function fitTextToLines(text, value, maxLines, wrapWidth) {
64
- const width = Math.max(1, wrapWidth);
65
- text.style.wordWrapWidth = width;
66
- if (!value || maxLines <= 0) {
67
- if (text.text !== "") text.text = "";
68
- return;
69
- }
70
- const charCap = Math.max(maxLines * Math.ceil(width), maxLines * 4);
71
- const sample = value.length > charCap ? value.slice(0, charCap) : value;
72
- const metrics = CanvasTextMetrics.measureText(sample, text.style);
73
- if (!(metrics.lines.length > maxLines || sample !== value)) {
74
- if (text.text !== value) text.text = value;
75
- return;
76
- }
77
- const keptCount = Math.min(maxLines, metrics.lines.length);
78
- const kept = metrics.lines.slice(0, keptCount);
79
- const lastIndex = kept.length - 1;
80
- const probe = text.style.clone();
81
- probe.wordWrap = false;
82
- kept[lastIndex] = fitLineWithEllipsis(kept[lastIndex] ?? "", width, (candidate) => CanvasTextMetrics.measureText(candidate, probe).width);
83
- const fitted = kept.join("\n");
84
- if (text.text !== fitted) text.text = fitted;
85
- }
86
34
  function linesInRoom(room, lineHeight, max) {
87
35
  if (room < lineHeight * .85) return 0;
88
36
  return Math.max(0, Math.min(max, Math.floor((room + .5) / lineHeight)));
89
37
  }
90
- function containCoverRect(width, height, imageWidth, imageHeight) {
91
- if (width <= 0 || height <= 0 || imageWidth <= 0 || imageHeight <= 0) return {
92
- x: 0,
93
- y: 0,
94
- width: 0,
95
- height: 0
96
- };
97
- const scale = Math.min(width / imageWidth, height / imageHeight);
98
- const renderedWidth = imageWidth * scale;
99
- const renderedHeight = imageHeight * scale;
100
- return {
101
- x: (width - renderedWidth) / 2,
102
- y: (height - renderedHeight) / 2,
103
- width: renderedWidth,
104
- height: renderedHeight
105
- };
106
- }
107
38
  function layoutCover(sprite, width, height, texture) {
108
- const rect = containCoverRect(width, height, texture.width, texture.height);
39
+ const rect = containRect(width, height, texture.width, texture.height);
109
40
  sprite.position.set(rect.x, rect.y);
110
41
  sprite.width = rect.width;
111
42
  sprite.height = rect.height;
112
43
  }
113
- function syncClip(parts, width, height) {
114
- parts.clip.clear().roundRect(0, 0, Math.max(1, width), Math.max(1, height), RADIUS).fill({ color: 16777215 });
44
+ function syncClip(parts, rect, style) {
45
+ traceCardOutline(parts.clip.clear(), rect, style).fill({ color: 16777215 });
115
46
  }
116
47
  function sync(container, item, context) {
117
48
  const parts = partsByContainer.get(container);
118
49
  if (!parts) return;
119
50
  positionShell(parts.root, item);
120
51
  const { width, height } = item.frame;
121
- const selected = context.selectedIds.has(item.id);
122
- const hovered = context.hoveredId === item.id;
123
52
  const detail = detailFor(context.zoom);
53
+ const elevation = cardElevation(context, item.id);
54
+ syncCardTag(parts.tag, {
55
+ glyph: "file",
56
+ text: fileMetaLine(item.props.src, item.props.snapshot?.size),
57
+ width
58
+ }, context);
124
59
  const key = context.assetKey(item);
125
60
  const texture = key ? context.getTexture(key) : null;
126
61
  const coverFailed = Boolean(key && !texture && context.hasError(key));
@@ -136,30 +71,30 @@ function sync(container, item, context) {
136
71
  key ?? "",
137
72
  width,
138
73
  height,
139
- selected,
140
- hovered,
141
74
  detail,
75
+ elevation,
142
76
  texture ? `${texture.width}x${texture.height}` : "none",
143
77
  coverFailed,
144
78
  fileState,
145
79
  kind,
146
80
  category,
147
- context.colorScheme,
148
- context.palette.surface,
149
- context.palette.hover,
81
+ context.themeKey,
150
82
  accent
151
83
  ].join("|");
152
84
  if (visualSig !== parts.visualSig) {
153
85
  parts.visualSig = visualSig;
154
- syncClip(parts, width, height);
155
- parts.plate.clear();
156
- parts.plate.roundRect(0, 0, width, height, RADIUS).fill({
157
- color: context.palette.surface,
158
- alpha: .98
159
- }).roundRect(0, 0, width, height, RADIUS).stroke({
160
- color: selected ? context.palette.brand : hovered ? context.palette.muted : context.palette.border,
161
- width: selected ? 2 : 1,
162
- alpha: selected ? .95 : .85
86
+ const rect = {
87
+ x: 0,
88
+ y: 0,
89
+ width,
90
+ height
91
+ };
92
+ syncClip(parts, rect, context.style);
93
+ drawCardSurface(parts.plate.clear(), rect, context.style, {
94
+ fill: context.palette.surface,
95
+ border: context.palette.border,
96
+ borderAlpha: .85,
97
+ elevation
163
98
  });
164
99
  if (fileState !== "ok") {
165
100
  const dash = Math.max(6, width / MAX_DASHES / 2);
@@ -178,8 +113,7 @@ function sync(container, item, context) {
178
113
  if (showCover && texture) {
179
114
  if (parts.cover.texture !== texture) parts.cover.texture = texture;
180
115
  layoutCover(parts.cover, width, band, texture);
181
- parts.coverMask.clear().roundRect(0, 0, width, band, RADIUS).fill({ color: 16777215 });
182
- parts.coverMask.rect(0, band - RADIUS, width, RADIUS).fill({ color: 16777215 });
116
+ parts.coverMask.clear().rect(0, 0, width, band).fill({ color: 16777215 });
183
117
  }
184
118
  parts.cover.visible = showCover;
185
119
  parts.coverMask.visible = showCover;
@@ -187,13 +121,8 @@ function sync(container, item, context) {
187
121
  color: accent,
188
122
  alpha: .55
189
123
  });
190
- parts.title.visible = detail !== "plate";
191
124
  parts.excerpt.visible = detail === "full";
192
- parts.typeMark.visible = kind === "blank" && detail !== "plate";
193
- }
194
- if (detail === "plate") {
195
- parts.typeMark.visible = false;
196
- return;
125
+ parts.typeMark.visible = kind === "blank";
197
126
  }
198
127
  const title = item.props.snapshot?.title || fileStem(item.props.src);
199
128
  const excerpt = item.props.snapshot?.excerpt ?? "";
@@ -211,10 +140,13 @@ function sync(container, item, context) {
211
140
  kind,
212
141
  context.palette.text,
213
142
  context.palette.muted,
143
+ context.style.font,
214
144
  accent
215
145
  ].join("|");
216
146
  if (textSig === parts.textSig) return;
217
147
  parts.textSig = textSig;
148
+ parts.title.style.fontFamily = context.style.font;
149
+ parts.excerpt.style.fontFamily = context.style.font;
218
150
  const top = band > 0 ? band + PADDING * .8 : PADDING;
219
151
  const contentBottom = height - PADDING;
220
152
  let cursor = top;
@@ -301,8 +233,10 @@ const fileCardRenderer = {
301
233
  });
302
234
  body.mask = clip;
303
235
  body.addChild(cover, coverMask, typeMark, title, excerpt);
304
- root.addChild(plate, body, clip);
236
+ const tag = createCardTag();
237
+ root.addChild(plate, body, clip, tag.root);
305
238
  partsByContainer.set(root, {
239
+ tag,
306
240
  root,
307
241
  plate,
308
242
  body,
@@ -324,6 +258,10 @@ const fileCardRenderer = {
324
258
  update: (container, item, context) => {
325
259
  if (item.type === "file") sync(container, item, context);
326
260
  },
261
+ zoom: (container, zoom) => {
262
+ const tag = partsByContainer.get(container)?.tag;
263
+ if (tag) zoomCardTag(tag, zoom);
264
+ },
327
265
  renderFar: (graphics, item, context) => {
328
266
  const category = fileCategory(item.type === "file" ? item.props.src : "", item.type === "file" ? item.props.snapshot?.mimeType : void 0);
329
267
  drawFarPlate(graphics, item.frame, {
@@ -342,4 +280,4 @@ function isFileItem(item) {
342
280
  return item.type === "file";
343
281
  }
344
282
  //#endregion
345
- export { containCoverRect, ellipsizeWrappedLines, fileCardRenderer, fitLineWithEllipsis, fitTextToLines, isFileItem };
283
+ export { fileCardRenderer, isFileItem };
@@ -1,9 +1,9 @@
1
1
  import { BOARD_FONT_STACK } from "../../../protocol/dist/board-constants.js";
2
2
  import { itemAlpha, itemColor } from "../palette.js";
3
3
  import { syncTextResolution } from "../text-resolution.js";
4
+ import { dashPattern, roundRectPoints, tracePolyline, trimPolyline } from "../stroke.js";
4
5
  import { createLabel, positionShell } from "./base-card-renderer.js";
5
6
  import { drawFarPlate } from "./far-plate.js";
6
- import { dashPattern, roundRectPoints, tracePolyline, trimPolyline } from "../stroke.js";
7
7
  import { Container, Graphics } from "pixi.js";
8
8
  //#region src/board/render/renderers/frame-card-renderer.ts
9
9
  const partsByContainer = /* @__PURE__ */ new WeakMap();
@@ -14,13 +14,10 @@ function sync(container, item, context) {
14
14
  const { frame, style, props, locked } = item;
15
15
  positionShell(parts.root, item);
16
16
  syncTextResolution(parts.label, parts, context.zoom);
17
- const selected = context.selectedIds.has(item.id);
18
- const hovered = context.hoveredId === item.id;
19
17
  const fill = itemColor(context, style.fill ?? style.stroke, "neutral", "fill");
20
18
  const fillAlpha = (style.fillOpacity ?? (style.fill ? 1 : DEFAULT_WASH)) * itemAlpha(context, style.fill ?? style.stroke);
21
19
  const baseStroke = itemColor(context, style.stroke, "neutral");
22
- const stroke = selected ? context.palette.brand : hovered && style.stroke ? context.palette.muted : baseStroke;
23
- const alpha = (selected || style.stroke ? 1 : hovered ? .85 : .55) * itemAlpha(context, style.stroke);
20
+ const alpha = (style.stroke ? 1 : .55) * itemAlpha(context, style.stroke);
24
21
  const radius = style.radius ?? 4;
25
22
  const strokeWidth = style.strokeWidth ?? 1.5;
26
23
  const decorated = props.decoration !== "none";
@@ -28,11 +25,9 @@ function sync(container, item, context) {
28
25
  props.decoration,
29
26
  frame.width,
30
27
  frame.height,
31
- selected,
32
- hovered,
33
28
  fill,
34
29
  fillAlpha,
35
- stroke,
30
+ baseStroke,
36
31
  alpha,
37
32
  radius,
38
33
  strokeWidth,
@@ -46,15 +41,14 @@ function sync(container, item, context) {
46
41
  color: fill,
47
42
  alpha: fillAlpha
48
43
  });
49
- const width = selected ? Math.max(2, strokeWidth) : strokeWidth;
50
- const pattern = decorated ? dashPattern(style.dash, width) : null;
51
- const trim = decorated ? style.trim ?? 1 : 1;
52
- if ((decorated && strokeWidth > 0 || selected || hovered) && trim > 0) {
44
+ const pattern = dashPattern(style.dash, strokeWidth);
45
+ const trim = style.trim ?? 1;
46
+ if (decorated && strokeWidth > 0 && trim > 0) {
53
47
  if (pattern || trim < 1) tracePolyline(parts.box, trimPolyline(roundRectPoints(frame.width, frame.height, radius), trim), pattern);
54
48
  else parts.box.roundRect(0, 0, frame.width, frame.height, radius);
55
49
  parts.box.stroke({
56
- color: stroke,
57
- width,
50
+ color: baseStroke,
51
+ width: strokeWidth,
58
52
  alpha,
59
53
  cap: pattern || trim < 1 ? "round" : "butt",
60
54
  join: "round"
@@ -64,11 +58,16 @@ function sync(container, item, context) {
64
58
  parts.label.visible = decorated;
65
59
  const title = locked ? `🔒 ${props.label}`.trim() : props.label;
66
60
  const labelColor = style.stroke ? baseStroke : context.palette.muted;
67
- const textSig = [title, labelColor].join("|");
61
+ const textSig = [
62
+ title,
63
+ labelColor,
64
+ context.style.font
65
+ ].join("|");
68
66
  if (textSig !== parts.textSig) {
69
67
  parts.textSig = textSig;
70
68
  parts.label.text = title;
71
69
  parts.label.style.fill = labelColor;
70
+ parts.label.style.fontFamily = context.style.font;
72
71
  }
73
72
  parts.label.position.set(4, -18);
74
73
  }
@@ -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 texId = texture ? `${texture.width}x${texture.height}` : "none";
49
+ const elevation = cardElevation(context, item.id);
62
50
  const sig = [
63
51
  key ?? "",
64
52
  width,
65
53
  height,
66
- selected,
67
- hovered,
68
- texId,
54
+ texture ? `${texture.width}x${texture.height}` : "none",
69
55
  JSON.stringify(item.props.crop ?? null),
70
56
  failed,
71
- context.colorScheme
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) layoutContain(parts.sprite, width, height);
78
- parts.placeholder.clear();
79
- if (!texture) {
80
- parts.placeholder.roundRect(0, 0, width, height, RADIUS).fill({
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 !== "image") return;
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 lineWidth = selected ? Math.max(2.5, strokeWidth) : strokeWidth;
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: hovered && !selected ? context.palette.muted : stroke,
143
- width: lineWidth,
144
- alpha: (selected ? 1 : hovered ? .95 : .85) * strokeAlpha,
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 { TASK_CARD_FULL_DETAIL_ZOOM, containTaskPreviewRect, formatTaskElapsed, taskCardRenderer };
5
+ export { taskCardRenderer };