chat-layout 1.1.0-4 → 1.1.0-5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -20,7 +20,7 @@ const bubble = new RoundedBox(
20
20
  new MultilineText(item.content, {
21
21
  lineHeight: 20,
22
22
  font: "16px system-ui",
23
- style: "black",
23
+ color: "black",
24
24
  align: "start",
25
25
  }),
26
26
  { top: 6, bottom: 6, left: 10, right: 10, radii: 8, fill: "#ccc" },
@@ -64,7 +64,7 @@ Single-line `Text` can ellipsize at the start, end, or middle when a finite widt
64
64
  const title = new Text("Extremely long thread title that should not blow out the row", {
65
65
  lineHeight: 20,
66
66
  font: "16px system-ui",
67
- style: "#111",
67
+ color: "#111",
68
68
  overflow: "ellipsis",
69
69
  ellipsisPosition: "middle",
70
70
  });
@@ -76,7 +76,7 @@ Multi-line `MultilineText` can cap the visible line count and convert the last v
76
76
  const preview = new MultilineText(reply.content, {
77
77
  lineHeight: 16,
78
78
  font: "13px system-ui",
79
- style: "#444",
79
+ color: "#444",
80
80
  align: "start",
81
81
  overflowWrap: "anywhere",
82
82
  overflow: "ellipsis",
package/example/chat.ts CHANGED
@@ -157,23 +157,23 @@ type ChatItem = MessageItem | RevokedItem;
157
157
 
158
158
  const richTextMessage: InlineSpan<C>[] = [
159
159
  { text: "现在这个 chat example 可以直接展示 " },
160
- { text: "rich text", font: "700 16px system-ui", style: "#0f766e" },
160
+ { text: "rich text", font: "700 16px system-ui", color: "#0f766e" },
161
161
  { text: " 了,支持 " },
162
- { text: "颜色", style: "#2563eb" },
162
+ { text: "颜色", color: "#2563eb" },
163
163
  { text: "、" },
164
- { text: "粗体", font: "700 16px system-ui", style: "#b91c1c" },
164
+ { text: "粗体", font: "700 16px system-ui", color: "#b91c1c" },
165
165
  { text: ",以及 " },
166
- { text: "inline code", font: "15px ui-monospace, SFMono-Regular, Consolas, monospace", style: "#7c3aed" },
166
+ { text: "inline code", font: "15px ui-monospace, SFMono-Regular, Consolas, monospace", color: "#7c3aed" },
167
167
  { text: " 这样的片段混排。" },
168
168
  ];
169
169
 
170
170
  const richReplyPreview: InlineSpan<C>[] = [
171
171
  { text: "回复预览里也能用 " },
172
- { text: "rich text", font: "700 13px system-ui", style: "#0f766e" },
172
+ { text: "rich text", font: "700 13px system-ui", color: "#0f766e" },
173
173
  { text: ",比如 " },
174
- { text: "关键词高亮", style: "#2563eb" },
174
+ { text: "关键词高亮", color: "#2563eb" },
175
175
  { text: " 和 " },
176
- { text: "code()", font: "12px ui-monospace, SFMono-Regular, Consolas, monospace", style: "#7c3aed" },
176
+ { text: "code()", font: "12px ui-monospace, SFMono-Regular, Consolas, monospace", color: "#7c3aed" },
177
177
  {
178
178
  text: ",超长内容仍然会按原来的两行省略规则收起,不需要额外处理。",
179
179
  },
@@ -225,7 +225,7 @@ const renderItem = memoRenderItem((item: ChatItem): Node<C> => {
225
225
  new Text(`${item.sender}已撤回一条消息`, {
226
226
  lineHeight: 18,
227
227
  font: "14px system-ui",
228
- style: () => (currentHover?.id === item.id ? "#525252" : "#666"),
228
+ color: () => (currentHover?.id === item.id ? "#525252" : "#666"),
229
229
  overflow: "ellipsis",
230
230
  }),
231
231
  {
@@ -262,7 +262,7 @@ const renderItem = memoRenderItem((item: ChatItem): Node<C> => {
262
262
  new Text(item.sender, {
263
263
  lineHeight: 15,
264
264
  font: "12px system-ui",
265
- style: "black",
265
+ color: "black",
266
266
  }),
267
267
  {
268
268
  top: 0,
@@ -286,7 +286,7 @@ const renderItem = memoRenderItem((item: ChatItem): Node<C> => {
286
286
  new MultilineText(item.content, {
287
287
  lineHeight: 20,
288
288
  font: "16px system-ui",
289
- style: "black",
289
+ color: "black",
290
290
  align: "start",
291
291
  overflowWrap: "anywhere",
292
292
  }),
@@ -302,12 +302,12 @@ const renderItem = memoRenderItem((item: ChatItem): Node<C> => {
302
302
  new Text(item.reply.sender, {
303
303
  lineHeight: 14,
304
304
  font: "11px system-ui",
305
- style: () => (currentHover?.id === item.id ? "#4d4d4d" : "#666"),
305
+ color: () => (currentHover?.id === item.id ? "#4d4d4d" : "#666"),
306
306
  }),
307
307
  new MultilineText(item.reply.content, {
308
308
  lineHeight: 16,
309
309
  font: "13px system-ui",
310
- style: () => (currentHover?.id === item.id ? "#222" : "#444"),
310
+ color: () => (currentHover?.id === item.id ? "#222" : "#444"),
311
311
  align: "start",
312
312
  overflow: "ellipsis",
313
313
  overflowWrap: "anywhere",
package/example/test.ts CHANGED
@@ -98,7 +98,7 @@ const node = new RoundedBox(
98
98
  lineHeight: 20,
99
99
  font: "400 16px monospace",
100
100
  align: "center",
101
- style: "black",
101
+ color: "black",
102
102
  }),
103
103
  { align: "center" },
104
104
  ),
@@ -108,10 +108,10 @@ const node = new RoundedBox(
108
108
  [
109
109
  new ClickDetect(
110
110
  new RoundedBox(
111
- new Text("测试3".repeat(2), {
111
+ new Text("测试 3".repeat(2), {
112
112
  lineHeight: 20,
113
113
  font: "400 16px monospace",
114
- style: () => color,
114
+ color: () => color,
115
115
  }),
116
116
  {
117
117
  left: 14,
@@ -128,7 +128,7 @@ const node = new RoundedBox(
128
128
  lineHeight: 16,
129
129
  font: "400 12px monospace",
130
130
  align: "center",
131
- style: "black",
131
+ color: "black",
132
132
  }),
133
133
  {
134
134
  left: 10,
@@ -163,7 +163,7 @@ const node = new RoundedBox(
163
163
  lineHeight: 20,
164
164
  font: "400 16px monospace",
165
165
  physicalAlign: "right",
166
- style: "black",
166
+ color: "black",
167
167
  }),
168
168
  {
169
169
  left: 10,
package/index.d.mts CHANGED
@@ -72,8 +72,8 @@ interface TextStyleOptions<C extends CanvasRenderingContext2D> {
72
72
  lineHeight: number;
73
73
  /** Canvas font string used for measurement and drawing. */
74
74
  font: string;
75
- /** Fill style or resolver used when drawing the text. */
76
- style: DynValue<C, string>;
75
+ /** Color or resolver used when drawing the text. */
76
+ color: DynValue<C, string>;
77
77
  /** Default: normal; matches pretext and CSS-style collapsible whitespace behavior. */
78
78
  whiteSpace?: TextWhiteSpaceMode;
79
79
  /** Default: normal; use keep-all to match pretext's CJK-friendly line breaking mode. */
@@ -89,8 +89,8 @@ interface InlineSpan<C extends CanvasRenderingContext2D> {
89
89
  text: string;
90
90
  /** Canvas font string override for this fragment. Falls back to the node-level font. */
91
91
  font?: string;
92
- /** Fill style override for this fragment. Falls back to the node-level style. */
93
- style?: DynValue<C, string>;
92
+ /** Color override for this fragment. Falls back to the node-level color. */
93
+ color?: DynValue<C, string>;
94
94
  /** Optional break hint forwarded to pretext rich-inline layout. */
95
95
  break?: "normal" | "never";
96
96
  /** Optional extra occupied width forwarded to pretext rich-inline layout. */
package/index.mjs CHANGED
@@ -1235,12 +1235,12 @@ function readRichPrepared(spans, defaultFont) {
1235
1235
  extraWidth: s.extraWidth
1236
1236
  }))), RICH_PREPARED_CACHE_CAPACITY);
1237
1237
  }
1238
- function materializeRichLine(ctx, spans, defaultFont, defaultStyle, lineRange, overflowed) {
1238
+ function materializeRichLine(ctx, spans, defaultFont, defaultColor, lineRange, overflowed) {
1239
1239
  const richLine = materializeRichInlineLineRange(readRichPrepared(spans, defaultFont), lineRange);
1240
1240
  const fragments = richLine.fragments.map((frag) => {
1241
1241
  const span = spans[frag.itemIndex];
1242
1242
  const fragFont = span?.font ?? defaultFont;
1243
- const fragStyle = span?.style ?? defaultStyle;
1243
+ const fragColor = span?.color ?? defaultColor;
1244
1244
  const prevFont = ctx.graphics.font;
1245
1245
  ctx.graphics.font = fragFont;
1246
1246
  const shift = measureFontShift(ctx);
@@ -1249,7 +1249,7 @@ function materializeRichLine(ctx, spans, defaultFont, defaultStyle, lineRange, o
1249
1249
  itemIndex: frag.itemIndex,
1250
1250
  text: frag.text,
1251
1251
  font: fragFont,
1252
- style: fragStyle,
1252
+ color: fragColor,
1253
1253
  gapBefore: frag.gapBefore,
1254
1254
  occupiedWidth: frag.occupiedWidth,
1255
1255
  shift
@@ -1305,7 +1305,7 @@ function measureRichTextMinContent(ctx, spans, defaultFont, overflowWrap = "brea
1305
1305
  lineCount
1306
1306
  };
1307
1307
  }
1308
- function layoutRichText(ctx, spans, maxWidth, defaultFont, defaultStyle) {
1308
+ function layoutRichText(ctx, spans, maxWidth, defaultFont, defaultColor) {
1309
1309
  if (spans.length === 0) return {
1310
1310
  width: 0,
1311
1311
  lines: [],
@@ -1319,24 +1319,24 @@ function layoutRichText(ctx, spans, maxWidth, defaultFont, defaultStyle) {
1319
1319
  lines: [],
1320
1320
  overflowed: false
1321
1321
  };
1322
- const lines = lineRanges.map((lr) => materializeRichLine(ctx, spans, defaultFont, defaultStyle, lr, false));
1322
+ const lines = lineRanges.map((lr) => materializeRichLine(ctx, spans, defaultFont, defaultColor, lr, false));
1323
1323
  return {
1324
1324
  width: lines.reduce((max, line) => Math.max(max, line.width), 0),
1325
1325
  lines,
1326
1326
  overflowed: false
1327
1327
  };
1328
1328
  }
1329
- function layoutRichTextIntrinsic(ctx, spans, defaultFont, defaultStyle) {
1330
- return layoutRichText(ctx, spans, INTRINSIC_MAX_WIDTH, defaultFont, defaultStyle);
1329
+ function layoutRichTextIntrinsic(ctx, spans, defaultFont, defaultColor) {
1330
+ return layoutRichText(ctx, spans, INTRINSIC_MAX_WIDTH, defaultFont, defaultColor);
1331
1331
  }
1332
- function layoutRichTextWithOverflow(ctx, spans, maxWidth, defaultFont, defaultStyle, maxLines, overflow = "clip") {
1332
+ function layoutRichTextWithOverflow(ctx, spans, maxWidth, defaultFont, defaultColor, maxLines, overflow = "clip") {
1333
1333
  if (spans.length === 0) return {
1334
1334
  width: 0,
1335
1335
  lines: [],
1336
1336
  overflowed: false
1337
1337
  };
1338
1338
  const normalizedMaxLines = normalizeMaxLines(maxLines);
1339
- const layout = layoutRichText(ctx, spans, maxWidth, defaultFont, defaultStyle);
1339
+ const layout = layoutRichText(ctx, spans, maxWidth, defaultFont, defaultColor);
1340
1340
  if (normalizedMaxLines == null || layout.lines.length <= normalizedMaxLines) return layout;
1341
1341
  const visibleLines = layout.lines.slice(0, normalizedMaxLines);
1342
1342
  if (overflow !== "ellipsis") return {
@@ -1371,7 +1371,7 @@ function layoutRichTextWithOverflow(ctx, spans, maxWidth, defaultFont, defaultSt
1371
1371
  const resultFragments = [];
1372
1372
  let usedWidth = 0;
1373
1373
  let ellipsisFont = lastFrag.font;
1374
- let ellipsisStyle = lastFrag.style;
1374
+ let ellipsisColor = lastFrag.color;
1375
1375
  let truncated = false;
1376
1376
  for (let fi = 0; fi < lastLine.fragments.length; fi++) {
1377
1377
  const frag = lastLine.fragments[fi];
@@ -1385,7 +1385,7 @@ function layoutRichTextWithOverflow(ctx, spans, maxWidth, defaultFont, defaultSt
1385
1385
  usedWidth += fragTotal;
1386
1386
  } else {
1387
1387
  ellipsisFont = frag.font;
1388
- ellipsisStyle = frag.style;
1388
+ ellipsisColor = frag.color;
1389
1389
  const remaining = budget - usedWidth - neededGap;
1390
1390
  if (remaining > 0 && frag.text.length > 0) {
1391
1391
  const units = getPreparedUnits(readPreparedText(frag.text, frag.font, "normal", "normal"));
@@ -1418,7 +1418,7 @@ function layoutRichTextWithOverflow(ctx, spans, maxWidth, defaultFont, defaultSt
1418
1418
  itemIndex: -1,
1419
1419
  text: ELLIPSIS_GLYPH,
1420
1420
  font: ellipsisFont,
1421
- style: ellipsisStyle,
1421
+ color: ellipsisColor,
1422
1422
  gapBefore: 0,
1423
1423
  occupiedWidth: ellipsisWidth,
1424
1424
  shift: ellipsisShift
@@ -1549,12 +1549,12 @@ function getRichMultiLineMeasureLayout(node, ctx, spans, options) {
1549
1549
  }
1550
1550
  function getRichMultiLineOverflowLayout(node, ctx, spans, options) {
1551
1551
  const maxWidth = normalizeTextMaxWidth(ctx.constraints?.maxWidth);
1552
- return readCachedTextLayout(node, ctx, getRichMultiLineOverflowLayoutKey(maxWidth), () => layoutRichTextWithOverflow(ctx, spans, maxWidth ?? 0, options.font, options.style, options.maxLines, options.overflow));
1552
+ return readCachedTextLayout(node, ctx, getRichMultiLineOverflowLayoutKey(maxWidth), () => layoutRichTextWithOverflow(ctx, spans, maxWidth ?? 0, options.font, options.color, options.maxLines, options.overflow));
1553
1553
  }
1554
1554
  function getRichMultiLineDrawLayout(node, ctx, spans, options) {
1555
1555
  const maxWidth = normalizeTextMaxWidth(ctx.constraints?.maxWidth);
1556
1556
  if (maxWidth != null && shouldUseMultilineOverflowLayout(options)) return getRichMultiLineOverflowLayout(node, ctx, spans, options);
1557
- return readCachedTextLayout(node, ctx, getRichMultiLineDrawLayoutKey(maxWidth), () => maxWidth == null ? layoutRichTextIntrinsic(ctx, spans, options.font, options.style) : layoutRichText(ctx, spans, maxWidth, options.font, options.style));
1557
+ return readCachedTextLayout(node, ctx, getRichMultiLineDrawLayoutKey(maxWidth), () => maxWidth == null ? layoutRichTextIntrinsic(ctx, spans, options.font, options.color) : layoutRichText(ctx, spans, maxWidth, options.font, options.color));
1558
1558
  }
1559
1559
  function getRichMultiLineMinContentLayout(node, ctx, spans, options) {
1560
1560
  return readCachedTextLayout(node, ctx, getRichMultiLineMinContentLayoutKey(), () => measureRichTextMinContent(ctx, spans, options.font, options.overflowWrap));
@@ -1621,7 +1621,7 @@ var MultilineText = class {
1621
1621
  cursorX += frag.gapBefore;
1622
1622
  ctx.with((g) => {
1623
1623
  g.font = frag.font;
1624
- g.fillStyle = ctx.resolveDynValue(frag.style ?? this.options.style);
1624
+ g.fillStyle = ctx.resolveDynValue(frag.color ?? this.options.color);
1625
1625
  if (align === "right") g.textAlign = "right";
1626
1626
  else if (align === "center") g.textAlign = "center";
1627
1627
  else g.textAlign = "left";
@@ -1635,7 +1635,7 @@ var MultilineText = class {
1635
1635
  }
1636
1636
  return ctx.with((g) => {
1637
1637
  g.font = this.options.font;
1638
- g.fillStyle = ctx.resolveDynValue(this.options.style);
1638
+ g.fillStyle = ctx.resolveDynValue(this.options.color);
1639
1639
  const { width, lines } = getMultiLineDrawLayout(this, ctx, this.text, this.options);
1640
1640
  switch (resolvePhysicalTextAlign(this.options)) {
1641
1641
  case "left":
@@ -1703,7 +1703,7 @@ var Text = class {
1703
1703
  draw(ctx, x, y) {
1704
1704
  return ctx.with((g) => {
1705
1705
  g.font = this.options.font;
1706
- g.fillStyle = ctx.resolveDynValue(this.options.style);
1706
+ g.fillStyle = ctx.resolveDynValue(this.options.color);
1707
1707
  const { text, shift } = getSingleLineLayout(this, ctx, this.text, this.options);
1708
1708
  g.fillText(text, x, y + (this.options.lineHeight + shift) / 2);
1709
1709
  return false;