@airalogy/aimd-renderer 2.5.0 → 2.6.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/README.md +6 -5
- package/README.zh-CN.md +6 -5
- package/dist/aimd-renderer.css +1 -1
- package/dist/common/processor.d.ts.map +1 -1
- package/dist/common/unified-token-renderer.d.ts.map +1 -1
- package/dist/html.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +262 -217
- package/dist/{processor-B2mByErv.js → processor-CHbNEcN8.js} +2687 -2094
- package/dist/vue/index.d.ts +1 -1
- package/dist/vue/index.d.ts.map +1 -1
- package/dist/vue/vue-renderer.d.ts +22 -1
- package/dist/vue/vue-renderer.d.ts.map +1 -1
- package/dist/vue.js +10 -9
- package/package.json +6 -6
- package/src/__tests__/renderer.test.ts +191 -1
- package/src/common/processor.ts +173 -41
- package/src/common/unified-token-renderer.ts +104 -24
- package/src/index.ts +3 -0
- package/src/styles/katex.css +109 -0
- package/src/vue/index.ts +3 -0
- package/src/vue/vue-renderer.ts +318 -48
package/src/vue/vue-renderer.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { Element, Root as HastRoot, Text as HastText, RootContent } from "hast"
|
|
2
2
|
import type { Component, VNode, VNodeChild } from "vue"
|
|
3
3
|
import type { AimdNode, AimdQuizNode, AimdStepNode, RenderContext } from "@airalogy/aimd-core/types"
|
|
4
|
+
import {
|
|
5
|
+
formatAimdExampleValue,
|
|
6
|
+
getAimdFieldDescription,
|
|
7
|
+
getAimdFieldDisplayLabel,
|
|
8
|
+
getAimdFieldExamples,
|
|
9
|
+
getAimdFieldTitle,
|
|
10
|
+
} from "@airalogy/aimd-core/utils"
|
|
4
11
|
import { Fragment, h } from "vue"
|
|
5
12
|
import type { AimdRendererI18nOptions, AimdRendererLocale, AimdRendererMessages } from "../locales"
|
|
6
13
|
import { resolveQuizPreviewOptions, type ResolvedQuizPreviewOptions } from "../common/quiz-preview"
|
|
@@ -152,6 +159,74 @@ function buildScaleBandChildren(quizNode: AimdQuizNode): VNodeChild[] {
|
|
|
152
159
|
]
|
|
153
160
|
}
|
|
154
161
|
|
|
162
|
+
interface FieldMetadataHelp {
|
|
163
|
+
tooltip: string
|
|
164
|
+
description?: string
|
|
165
|
+
examples: string[]
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function getFieldHelpText(definition: { kwargs?: Record<string, unknown> } | undefined): FieldMetadataHelp {
|
|
169
|
+
const description = getAimdFieldDescription(definition)
|
|
170
|
+
const examples = getAimdFieldExamples(definition)
|
|
171
|
+
.map(formatAimdExampleValue)
|
|
172
|
+
.map(example => example.trim())
|
|
173
|
+
.filter(Boolean)
|
|
174
|
+
const exampleText = examples.length > 0 ? `e.g. ${examples.join(", ")}` : undefined
|
|
175
|
+
const tooltipLines = [description, exampleText].filter((value): value is string => Boolean(value))
|
|
176
|
+
|
|
177
|
+
return {
|
|
178
|
+
tooltip: tooltipLines.join("\n"),
|
|
179
|
+
description,
|
|
180
|
+
examples,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function renderFieldMetadataPopover(help: FieldMetadataHelp): VNode | null {
|
|
185
|
+
if (!help.description && help.examples.length === 0) {
|
|
186
|
+
return null
|
|
187
|
+
}
|
|
188
|
+
const children: VNode[] = []
|
|
189
|
+
if (help.description) {
|
|
190
|
+
children.push(h("span", {
|
|
191
|
+
class: "aimd-field__metadata-popover-line",
|
|
192
|
+
}, help.description))
|
|
193
|
+
}
|
|
194
|
+
if (help.examples.length > 0) {
|
|
195
|
+
children.push(h("span", { class: "aimd-field__metadata-examples" }, [
|
|
196
|
+
h("span", { class: "aimd-field__metadata-examples-label" }, "e.g."),
|
|
197
|
+
...help.examples.map((example, index) => h("span", {
|
|
198
|
+
key: `${index}-${example}`,
|
|
199
|
+
class: "aimd-field__metadata-example",
|
|
200
|
+
}, example)),
|
|
201
|
+
]))
|
|
202
|
+
}
|
|
203
|
+
return h("span", {
|
|
204
|
+
class: "aimd-field__metadata-popover",
|
|
205
|
+
role: "tooltip",
|
|
206
|
+
}, children)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function renderFieldName(id: string, definition: { kwargs?: Record<string, unknown> } | undefined): VNode {
|
|
210
|
+
const displayTitle = getAimdFieldDisplayLabel(id, definition)
|
|
211
|
+
const hasCustomTitle = getAimdFieldTitle(definition) !== undefined && displayTitle !== id
|
|
212
|
+
const help = getFieldHelpText(definition)
|
|
213
|
+
const hasHelp = Boolean(help.description) || help.examples.length > 0
|
|
214
|
+
|
|
215
|
+
return h("span", {
|
|
216
|
+
class: [
|
|
217
|
+
"aimd-field__name",
|
|
218
|
+
(hasCustomTitle || hasHelp) ? "aimd-field__name--with-metadata" : undefined,
|
|
219
|
+
hasHelp ? "aimd-field__metadata-host" : undefined,
|
|
220
|
+
],
|
|
221
|
+
tabindex: hasHelp ? 0 : undefined,
|
|
222
|
+
"aria-label": help.tooltip || undefined,
|
|
223
|
+
}, [
|
|
224
|
+
h("span", { class: "aimd-field__title" }, displayTitle),
|
|
225
|
+
hasCustomTitle ? h("span", { class: "aimd-field__key" }, id) : null,
|
|
226
|
+
renderFieldMetadataPopover(help),
|
|
227
|
+
])
|
|
228
|
+
}
|
|
229
|
+
|
|
155
230
|
function isStepBodyVNode(node: unknown): node is VNode {
|
|
156
231
|
if (!node || typeof node !== "object") {
|
|
157
232
|
return false
|
|
@@ -209,11 +284,11 @@ const defaultAimdRenderers: Record<string, AimdComponentRenderer> = {
|
|
|
209
284
|
"data-aimd-type": "var",
|
|
210
285
|
"data-aimd-id": id,
|
|
211
286
|
"data-aimd-scope": scope,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
287
|
+
}, [
|
|
288
|
+
h("span", { class: "aimd-field__scope" }, getAimdRendererScopeLabel(scope, ctx.messages)),
|
|
289
|
+
renderFieldName(id, definition),
|
|
290
|
+
definition?.type ? h("span", { class: "aimd-field__type" }, `: ${definition.type}`) : null,
|
|
291
|
+
])
|
|
217
292
|
}
|
|
218
293
|
|
|
219
294
|
// Edit mode - render as editable field with value display
|
|
@@ -236,24 +311,28 @@ const defaultAimdRenderers: Record<string, AimdComponentRenderer> = {
|
|
|
236
311
|
},
|
|
237
312
|
|
|
238
313
|
var_table: (node, ctx) => {
|
|
239
|
-
|
|
240
|
-
|
|
314
|
+
const { id } = node
|
|
315
|
+
const columns = "columns" in node ? node.columns : []
|
|
316
|
+
const definition = "definition" in node ? node.definition : undefined
|
|
317
|
+
const subvarDefs = definition?.subvars
|
|
241
318
|
|
|
242
|
-
|
|
319
|
+
if (ctx.mode === "preview") {
|
|
243
320
|
// Preview mode: render tag with table preview inside
|
|
244
321
|
const children: VNodeChild[] = [
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
322
|
+
h("div", { class: "aimd-field__header" }, [
|
|
323
|
+
h("span", { class: "aimd-field__scope" }, ctx.messages.scope.table),
|
|
324
|
+
renderFieldName(id, definition),
|
|
325
|
+
]),
|
|
326
|
+
]
|
|
250
327
|
// Add table preview inside the container
|
|
251
328
|
if (columns && columns.length > 0) {
|
|
252
329
|
children.push(
|
|
253
330
|
h("table", { class: "aimd-field__table-preview" }, [
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
331
|
+
h("thead", [
|
|
332
|
+
h("tr", columns.map(col => h("th", {
|
|
333
|
+
"data-column-id": col,
|
|
334
|
+
}, [renderFieldName(col, subvarDefs?.[col])]))),
|
|
335
|
+
]),
|
|
257
336
|
h("tbody", [
|
|
258
337
|
h("tr", columns.map(() => h("td", "..."))),
|
|
259
338
|
]),
|
|
@@ -1285,9 +1364,167 @@ export function createStepCardRenderer(
|
|
|
1285
1364
|
*/
|
|
1286
1365
|
export interface ShikiHighlighter {
|
|
1287
1366
|
codeToHtml: (code: string, options: { lang: string, theme: string }) => string
|
|
1367
|
+
codeToTokensBase?: (code: string, options: { lang: string, theme: string }) => Array<Array<{ content: string, color?: string, bgColor?: string, fontStyle?: number, htmlStyle?: string | Record<string, string> }>>
|
|
1288
1368
|
codeToTokensWithThemes?: (code: string, options: { lang: string, themes: Record<string, string> }) => Array<Array<{ content: string, variants: Record<string, { color: string }> }>>
|
|
1289
1369
|
}
|
|
1290
1370
|
|
|
1371
|
+
export interface CodeBlockRendererOptions {
|
|
1372
|
+
theme?: string
|
|
1373
|
+
lineNumbers?: boolean
|
|
1374
|
+
wrap?: boolean
|
|
1375
|
+
className?: string
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
export interface LoadShikiHighlighterOptions {
|
|
1379
|
+
themes?: string[]
|
|
1380
|
+
langs?: string[]
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
type CodeBlockToken = {
|
|
1384
|
+
content: string
|
|
1385
|
+
color?: string
|
|
1386
|
+
bgColor?: string
|
|
1387
|
+
fontStyle?: number
|
|
1388
|
+
htmlStyle?: string | Record<string, string>
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
const DEFAULT_CODE_BLOCK_THEME = "github-light"
|
|
1392
|
+
const DEFAULT_CODE_HIGHLIGHTER_THEMES = [DEFAULT_CODE_BLOCK_THEME]
|
|
1393
|
+
const DEFAULT_CODE_HIGHLIGHTER_LANGS = [
|
|
1394
|
+
"bash",
|
|
1395
|
+
"css",
|
|
1396
|
+
"html",
|
|
1397
|
+
"javascript",
|
|
1398
|
+
"json",
|
|
1399
|
+
"jsonc",
|
|
1400
|
+
"markdown",
|
|
1401
|
+
"python",
|
|
1402
|
+
"shellscript",
|
|
1403
|
+
"sql",
|
|
1404
|
+
"toml",
|
|
1405
|
+
"typescript",
|
|
1406
|
+
"xml",
|
|
1407
|
+
"yaml",
|
|
1408
|
+
]
|
|
1409
|
+
let defaultCodeHighlighterPromise: Promise<ShikiHighlighter> | null = null
|
|
1410
|
+
|
|
1411
|
+
export async function loadShikiHighlighter(
|
|
1412
|
+
options: LoadShikiHighlighterOptions = {},
|
|
1413
|
+
): Promise<ShikiHighlighter> {
|
|
1414
|
+
const hasCustomOptions = Boolean(options.themes || options.langs)
|
|
1415
|
+
const create = async () => {
|
|
1416
|
+
const { createHighlighter } = await import("shiki")
|
|
1417
|
+
return createHighlighter({
|
|
1418
|
+
themes: (options.themes ?? DEFAULT_CODE_HIGHLIGHTER_THEMES) as never,
|
|
1419
|
+
langs: (options.langs ?? DEFAULT_CODE_HIGHLIGHTER_LANGS) as never,
|
|
1420
|
+
}) as Promise<unknown> as Promise<ShikiHighlighter>
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
if (hasCustomOptions) {
|
|
1424
|
+
return create()
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
defaultCodeHighlighterPromise ??= create()
|
|
1428
|
+
return defaultCodeHighlighterPromise
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
function resolveCodeBlockRendererOptions(
|
|
1432
|
+
optionsOrTheme: string | CodeBlockRendererOptions | undefined,
|
|
1433
|
+
): Required<CodeBlockRendererOptions> {
|
|
1434
|
+
if (typeof optionsOrTheme === "string") {
|
|
1435
|
+
return {
|
|
1436
|
+
theme: optionsOrTheme,
|
|
1437
|
+
lineNumbers: false,
|
|
1438
|
+
wrap: false,
|
|
1439
|
+
className: "",
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
return {
|
|
1444
|
+
theme: optionsOrTheme?.theme ?? DEFAULT_CODE_BLOCK_THEME,
|
|
1445
|
+
lineNumbers: optionsOrTheme?.lineNumbers ?? false,
|
|
1446
|
+
wrap: optionsOrTheme?.wrap ?? false,
|
|
1447
|
+
className: optionsOrTheme?.className ?? "",
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
function getCodeLanguage(codeNode: Element): string {
|
|
1452
|
+
const className = codeNode.properties?.className
|
|
1453
|
+
if (Array.isArray(className)) {
|
|
1454
|
+
const langClass = className.find(c => typeof c === "string" && c.startsWith("language-"))
|
|
1455
|
+
return typeof langClass === "string" ? langClass.replace("language-", "") : "text"
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
return typeof className === "string" && className.startsWith("language-")
|
|
1459
|
+
? className.replace("language-", "")
|
|
1460
|
+
: "text"
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
function getCodeContent(codeNode: Element): string {
|
|
1464
|
+
return codeNode.children
|
|
1465
|
+
.map(child => (child.type === "text" ? child.value : ""))
|
|
1466
|
+
.join("")
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
function normalizeCodeContent(code: string): string {
|
|
1470
|
+
return code.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\n$/, "")
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
function splitCodeLines(code: string): CodeBlockToken[][] {
|
|
1474
|
+
const lines = code.split(/\r\n|\r|\n/)
|
|
1475
|
+
return (lines.length ? lines : [""]).map(line => [{ content: line }])
|
|
1476
|
+
}
|
|
1477
|
+
|
|
1478
|
+
function getLineIndentColumns(tokens: CodeBlockToken[]): number {
|
|
1479
|
+
const line = tokens.map(token => token.content).join("")
|
|
1480
|
+
let columns = 0
|
|
1481
|
+
for (const char of line) {
|
|
1482
|
+
if (char === " ") {
|
|
1483
|
+
columns += 1
|
|
1484
|
+
}
|
|
1485
|
+
else if (char === "\t") {
|
|
1486
|
+
columns += 2
|
|
1487
|
+
}
|
|
1488
|
+
else {
|
|
1489
|
+
break
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
return Math.min(columns, 24)
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
function hasCodeTokenContent(tokens: CodeBlockToken[]): boolean {
|
|
1496
|
+
return tokens.some(token => token.content.length > 0)
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
function getCodeTokenStyle(token: CodeBlockToken): string | Record<string, string> | undefined {
|
|
1500
|
+
if (token.htmlStyle) {
|
|
1501
|
+
return token.htmlStyle
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
const style: Record<string, string> = {}
|
|
1505
|
+
if (token.color) {
|
|
1506
|
+
style.color = token.color
|
|
1507
|
+
}
|
|
1508
|
+
if (token.bgColor) {
|
|
1509
|
+
style.backgroundColor = token.bgColor
|
|
1510
|
+
}
|
|
1511
|
+
if (typeof token.fontStyle === "number") {
|
|
1512
|
+
if (token.fontStyle & 1) style.fontStyle = "italic"
|
|
1513
|
+
if (token.fontStyle & 2) style.fontWeight = "700"
|
|
1514
|
+
if (token.fontStyle & 4) style.textDecoration = "underline"
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1517
|
+
return Object.keys(style).length > 0 ? style : undefined
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
function renderLegacyPlainCodeBlock(lang: string, codeContent: string): VNode {
|
|
1521
|
+
return h("pre", {
|
|
1522
|
+
class: `language-${lang}`,
|
|
1523
|
+
}, h("code", {
|
|
1524
|
+
class: `language-${lang}`,
|
|
1525
|
+
}, codeContent))
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1291
1528
|
/**
|
|
1292
1529
|
* Create code block element renderer with Shiki support
|
|
1293
1530
|
* @param highlighter - Shiki highlighter instance (can be reactive ref)
|
|
@@ -1295,8 +1532,11 @@ export interface ShikiHighlighter {
|
|
|
1295
1532
|
*/
|
|
1296
1533
|
export function createCodeBlockRenderer(
|
|
1297
1534
|
highlighter: ShikiHighlighter | null | (() => ShikiHighlighter | null),
|
|
1298
|
-
|
|
1535
|
+
optionsOrTheme: string | CodeBlockRendererOptions = DEFAULT_CODE_BLOCK_THEME,
|
|
1299
1536
|
): ElementRenderer {
|
|
1537
|
+
const options = resolveCodeBlockRendererOptions(optionsOrTheme)
|
|
1538
|
+
const useLegacyHtmlOutput = typeof optionsOrTheme === "string"
|
|
1539
|
+
|
|
1300
1540
|
return (node, children, ctx) => {
|
|
1301
1541
|
// Find code element inside pre
|
|
1302
1542
|
const codeNode = node.children.find(
|
|
@@ -1307,39 +1547,38 @@ export function createCodeBlockRenderer(
|
|
|
1307
1547
|
return h("pre", {}, children)
|
|
1308
1548
|
}
|
|
1309
1549
|
|
|
1310
|
-
|
|
1311
|
-
const
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1550
|
+
const lang = getCodeLanguage(codeNode)
|
|
1551
|
+
const codeContent = normalizeCodeContent(getCodeContent(codeNode))
|
|
1552
|
+
const hl = typeof highlighter === "function" ? highlighter() : highlighter
|
|
1553
|
+
let tokenLines = splitCodeLines(codeContent)
|
|
1554
|
+
|
|
1555
|
+
if (useLegacyHtmlOutput) {
|
|
1556
|
+
if (hl?.codeToHtml) {
|
|
1557
|
+
try {
|
|
1558
|
+
const highlightedHtml = hl.codeToHtml(codeContent, {
|
|
1559
|
+
lang,
|
|
1560
|
+
theme: options.theme,
|
|
1561
|
+
})
|
|
1562
|
+
|
|
1563
|
+
return h("div", {
|
|
1564
|
+
"class": "shiki-code-block",
|
|
1565
|
+
"data-lang": lang,
|
|
1566
|
+
"innerHTML": highlightedHtml,
|
|
1567
|
+
})
|
|
1568
|
+
}
|
|
1569
|
+
catch (error) {
|
|
1570
|
+
console.error("Failed to highlight code:", error)
|
|
1571
|
+
}
|
|
1317
1572
|
}
|
|
1318
|
-
}
|
|
1319
|
-
else if (typeof className === "string" && className.startsWith("language-")) {
|
|
1320
|
-
lang = className.replace("language-", "")
|
|
1321
|
-
}
|
|
1322
|
-
|
|
1323
|
-
// Get code content
|
|
1324
|
-
const codeContent = codeNode.children
|
|
1325
|
-
.map(child => (child.type === "text" ? child.value : ""))
|
|
1326
|
-
.join("")
|
|
1327
1573
|
|
|
1328
|
-
|
|
1329
|
-
|
|
1574
|
+
return renderLegacyPlainCodeBlock(lang, codeContent)
|
|
1575
|
+
}
|
|
1330
1576
|
|
|
1331
|
-
|
|
1332
|
-
if (hl) {
|
|
1577
|
+
if (hl?.codeToTokensBase) {
|
|
1333
1578
|
try {
|
|
1334
|
-
|
|
1579
|
+
tokenLines = hl.codeToTokensBase(codeContent, {
|
|
1335
1580
|
lang,
|
|
1336
|
-
theme:
|
|
1337
|
-
})
|
|
1338
|
-
|
|
1339
|
-
return h("div", {
|
|
1340
|
-
"class": "shiki-code-block",
|
|
1341
|
-
"data-lang": lang,
|
|
1342
|
-
"innerHTML": highlightedHtml,
|
|
1581
|
+
theme: options.theme,
|
|
1343
1582
|
})
|
|
1344
1583
|
}
|
|
1345
1584
|
catch (error) {
|
|
@@ -1347,9 +1586,40 @@ export function createCodeBlockRenderer(
|
|
|
1347
1586
|
}
|
|
1348
1587
|
}
|
|
1349
1588
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1589
|
+
return h("pre", {
|
|
1590
|
+
"class": [
|
|
1591
|
+
"aimd-code-block",
|
|
1592
|
+
options.lineNumbers ? "aimd-code-block--line-numbers" : "",
|
|
1593
|
+
options.wrap ? "aimd-code-block--wrap" : "",
|
|
1594
|
+
options.className,
|
|
1595
|
+
`language-${lang}`,
|
|
1596
|
+
].filter(Boolean).join(" "),
|
|
1597
|
+
"data-lang": lang,
|
|
1598
|
+
}, h("code", {
|
|
1599
|
+
class: [
|
|
1600
|
+
"aimd-code-block__code",
|
|
1601
|
+
`language-${lang}`,
|
|
1602
|
+
],
|
|
1603
|
+
}, tokenLines.map((lineTokens, lineIndex) => h("span", {
|
|
1604
|
+
class: "aimd-code-block__line",
|
|
1605
|
+
"data-line": String(lineIndex + 1),
|
|
1606
|
+
}, [
|
|
1607
|
+
options.lineNumbers
|
|
1608
|
+
? h("span", {
|
|
1609
|
+
class: "aimd-code-block__line-number",
|
|
1610
|
+
"aria-hidden": "true",
|
|
1611
|
+
}, String(lineIndex + 1))
|
|
1612
|
+
: null,
|
|
1613
|
+
h("span", {
|
|
1614
|
+
class: "aimd-code-block__line-code",
|
|
1615
|
+
style: { "--aimd-code-wrap-indent": `${getLineIndentColumns(lineTokens)}ch` },
|
|
1616
|
+
}, hasCodeTokenContent(lineTokens)
|
|
1617
|
+
? lineTokens.map((token, tokenIndex) => h("span", {
|
|
1618
|
+
key: `${lineIndex}:${tokenIndex}`,
|
|
1619
|
+
style: getCodeTokenStyle(token),
|
|
1620
|
+
}, token.content))
|
|
1621
|
+
: "\u00a0"),
|
|
1622
|
+
]))))
|
|
1353
1623
|
}
|
|
1354
1624
|
}
|
|
1355
1625
|
|