dsh-markdown-bubble 0.1.1 → 0.1.3
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 +4 -0
- package/package.json +1 -1
- package/src/client.js +178 -37
- package/src/index.js +1 -1
package/README.md
CHANGED
|
@@ -95,6 +95,8 @@ const a = 1
|
|
|
95
95
|
|
|
96
96
|
### 修复记录
|
|
97
97
|
|
|
98
|
+
- **0.1.3** —— 把「座位 render 绝不抛」这条不变式补完:0.1.2 守住了合成与文案查询,但仍有形状会在**座位自己的渲染里**抛错(`node` 缺失 → `node.data`、`props` 缺失 → 解构本身、`new Date(time)` 遇到 Symbol / BigInt / 抛错的 valueOf、宿主图片渲染器抛错)。这些同样冒泡到槽位机制、退掉整条条目,症状与 0.1.2 修的完全一样。现在三层兜底:座位本体一层 try 并把 `node` / `data` 的取用改成防御式;时钟与附件卡在各自调用点兜底;最后一层 `renderPlainRow` 保证最坏情况只是这一行退回纯文本,而操作条与兄弟插件锚点仍在。新增回归:21 种恶意宿主形状逐一断言不抛、且行与操作条都在。
|
|
99
|
+
- **0.1.2** — 座位组件不再可能因渲染期异常被槽位机制摘除:`composeUserMarkdown` / `buildMarkdownLabels` 在座位渲染过程中运行,抛错不会被错误边界接住(错误边界只能接住其后代,接不住创建它的那次渲染),会冒泡到槽位机制并把该条目整条退掉——屏幕上就是气泡交还给宿主、Markdown 变回源码。现在合成与所有文案查询都在座位内部兜底:任何宿主形状不符(节点结构异常、投影返回不可识别、locale 席位不可调用)都退回宿主原始投影,气泡外壳、操作条与兄弟插件锚点全部保留。`data.content` 非数组、`t` 不可调用等情形一并加进回归测试。
|
|
98
100
|
- **0.1.1** — 修复两个引用 chip 之间只有空白时该空白被当作空 Markdown 块丢弃的问题:相邻 chip 不再粘成 `@a@b`,chip 之间的换行也按宿主 `pre-wrap` 气泡的样子保留为换行(空白段渲染为 `data-dshmb-gap` 的预换行节点,仍不产生空 Markdown 块);自建的操作条 / 复制按钮补上 `data-dshmb-actions` / `data-dshmb-action` 命名空间标记;座位优先级 `-1` 的宿主依据(宿主默认 0、最低者渲染、同优先级抛错)与三个兄弟插件依赖的锚点结构一并加进回归测试。
|
|
99
101
|
|
|
100
102
|
### License
|
|
@@ -177,6 +179,8 @@ Queued-message previews, the composer itself and the goal panel stay plain text
|
|
|
177
179
|
|
|
178
180
|
### Fixes
|
|
179
181
|
|
|
182
|
+
- **0.1.3** — completes the "the seat render never throws" invariant: 0.1.2 guarded composition and the copy lookups, but shapes still threw in the seat's own render (an absent `node` -> `node.data`, absent props -> the destructuring itself, a `time` the Date constructor refuses — Symbol, BigInt, a `valueOf` that throws — and a host image renderer that throws). Each escaped to the slot machinery and retired the entry, the same symptom 0.1.2 fixed. Three layers now: one try around the seat body with defensive `node` / `data` access, per-call guards for the clock and the attachment card, and a last-resort `renderPlainRow` so the worst case is one row falling back to plain text with its action strip and the sibling anchors intact. Regression: 21 hostile host shapes, each asserted not to throw and to keep the row and its strip.
|
|
183
|
+
- **0.1.2** — the seat component can no longer be retired by a render-time exception: `composeUserMarkdown` / `buildMarkdownLabels` run while the seat renders, and a throw there is not caught by the error boundary (a boundary catches its descendants, not the render that creates it) — it escapes to the slot machinery, which drops the whole entry and hands the row back to the host, so the bubble reverts to raw source on screen. Composition and every copy lookup now degrade inside the seat: any host-shape mismatch (an unexpected node, an unrecognised projection result, a locale seat that is not callable) falls back to the host's plain projection while the bubble shell, the action strip and the sibling-plugin anchors stay intact. `data.content` that is not an array and an uncallable `t` are covered by regression tests.
|
|
180
184
|
- **0.1.1** — fixed whitespace-only plain runs between two reference chips being dropped as an empty Markdown block: adjacent chips no longer glue into `@a@b`, and a newline between two chips stays a line break the way the host's `pre-wrap` bubble shows it (the run renders as a `data-dshmb-gap` pre-wrap node, still never an empty Markdown block); the self-created action strip and copy button now carry the `data-dshmb-actions` / `data-dshmb-action` namespace markers; and the host evidence behind `priority: -1` (host default 0, lowest renders, same priority throws) plus the anchor structure the three sibling plugins rely on are covered by regression tests.
|
|
181
185
|
|
|
182
186
|
### License
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-markdown-bubble",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Markdown rendering for DeepSeek Harness sent user messages: every message bubble renders lists, code, tables, quotes and inline formatting through the host's own Markdown pipeline, while queues, previews and the composer keep their plain projection.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
package/src/client.js
CHANGED
|
@@ -49,7 +49,7 @@ window.__ModuleLoader__.load({
|
|
|
49
49
|
const NS = 'dsh-markdown-bubble'
|
|
50
50
|
|
|
51
51
|
/** Keep in sync with package.json and src/index.js. */
|
|
52
|
-
const PLUGIN_VERSION = '0.1.
|
|
52
|
+
const PLUGIN_VERSION = '0.1.3'
|
|
53
53
|
|
|
54
54
|
/** Presence marker the live verifier reads. */
|
|
55
55
|
const DEBUG_KEY = '__DSH_MARKDOWN_BUBBLE__'
|
|
@@ -230,6 +230,25 @@ window.__ModuleLoader__.load({
|
|
|
230
230
|
return react.isValidElement(piece) && typeof piece.props.children === 'string' ? piece.props.children : ''
|
|
231
231
|
}
|
|
232
232
|
|
|
233
|
+
/**
|
|
234
|
+
* A copy lookup that cannot take the seat down. The framework injects a
|
|
235
|
+
* `t` for the declared `chat` namespace, but a host-shape mismatch (a
|
|
236
|
+
* locale service that was never bound, a renamed key) must fall back to
|
|
237
|
+
* the key rather than throw during render.
|
|
238
|
+
* @param t - the injected chat translator, possibly not callable.
|
|
239
|
+
* @param key - the dictionary key.
|
|
240
|
+
* @param params - template parameters.
|
|
241
|
+
* @returns the translated string, or the key.
|
|
242
|
+
*/
|
|
243
|
+
function safeTranslate(t, key, params) {
|
|
244
|
+
if (typeof t !== 'function') return key
|
|
245
|
+
try {
|
|
246
|
+
return t(key, params)
|
|
247
|
+
} catch {
|
|
248
|
+
return key
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
233
252
|
/** The host projection, called in a guard: it is the fallback of last resort. */
|
|
234
253
|
function plainProjection(text, referenceLabels, skillNames, references) {
|
|
235
254
|
try {
|
|
@@ -240,6 +259,36 @@ window.__ModuleLoader__.load({
|
|
|
240
259
|
}
|
|
241
260
|
}
|
|
242
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Compose the markdown body without ever throwing.
|
|
264
|
+
*
|
|
265
|
+
* `composeUserMarkdown` runs while the seat component renders, so a throw
|
|
266
|
+
* here does NOT reach {@link MarkdownBoundary} — a boundary catches its
|
|
267
|
+
* descendants, not the parent render that creates it. It would escape to
|
|
268
|
+
* the slot machinery, which answers by retiring the whole entry and handing
|
|
269
|
+
* the row back to the host's raw renderer; on screen that is
|
|
270
|
+
* indistinguishable from "the plugin stopped working". A host-shape
|
|
271
|
+
* mismatch (an unexpected node shape, an unrecognised projection result)
|
|
272
|
+
* therefore falls back to the plain projection right here, inside our own
|
|
273
|
+
* render, so the bubble keeps its shell, its actions and the sibling
|
|
274
|
+
* plugins' injection anchors.
|
|
275
|
+
* @param text - the message text.
|
|
276
|
+
* @param t - the injected chat translator.
|
|
277
|
+
* @param referenceLabels - session recall labels.
|
|
278
|
+
* @param skillNames - skill names.
|
|
279
|
+
* @param references - file/skill open callbacks.
|
|
280
|
+
* @returns the composed body, or null when markdown cannot be produced.
|
|
281
|
+
*/
|
|
282
|
+
function safeMarkdownBody(text, t, referenceLabels, skillNames, references) {
|
|
283
|
+
try {
|
|
284
|
+
const labels = buildMarkdownLabels(t)
|
|
285
|
+
return composeUserMarkdown(text, labels, referenceLabels, skillNames, references)
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.warn('[' + NS + '] markdown composition failed; showing the plain projection', error)
|
|
288
|
+
return null
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
243
292
|
/**
|
|
244
293
|
* A whitespace-only plain run: the gap between two chips, or the leading /
|
|
245
294
|
* trailing padding the host's own pre-wrap bubble shows. It carries no
|
|
@@ -330,15 +379,15 @@ window.__ModuleLoader__.load({
|
|
|
330
379
|
function buildMarkdownLabels(t) {
|
|
331
380
|
return {
|
|
332
381
|
code: {
|
|
333
|
-
copyLabel: t
|
|
334
|
-
copiedLabel: t
|
|
382
|
+
copyLabel: safeTranslate(t, 'copy'),
|
|
383
|
+
copiedLabel: safeTranslate(t, 'copied'),
|
|
335
384
|
toolbarLabels: {
|
|
336
|
-
codeLabel: t
|
|
337
|
-
wrapLabel: t
|
|
338
|
-
unwrapLabel: t
|
|
385
|
+
codeLabel: safeTranslate(t, 'codeBlock.title'),
|
|
386
|
+
wrapLabel: safeTranslate(t, 'codeBlock.wrap'),
|
|
387
|
+
unwrapLabel: safeTranslate(t, 'codeBlock.unwrap'),
|
|
339
388
|
},
|
|
340
389
|
},
|
|
341
|
-
footnotes: t
|
|
390
|
+
footnotes: safeTranslate(t, 'markdown.footnotes'),
|
|
342
391
|
}
|
|
343
392
|
}
|
|
344
393
|
|
|
@@ -385,6 +434,23 @@ window.__ModuleLoader__.load({
|
|
|
385
434
|
return day
|
|
386
435
|
}
|
|
387
436
|
|
|
437
|
+
/**
|
|
438
|
+
* Log one degradation per distinct reason.
|
|
439
|
+
*
|
|
440
|
+
* The seat renders on every transcript update, so a hostile host shape would
|
|
441
|
+
* otherwise print the same warning on every pass and bury the one line that
|
|
442
|
+
* matters. Keyed by the reason rather than by the row: what a reader needs to
|
|
443
|
+
* know is which guard fired, once.
|
|
444
|
+
* @param reason - one line naming what degraded.
|
|
445
|
+
* @param error - the caught value.
|
|
446
|
+
*/
|
|
447
|
+
const warnedReasons = new Set()
|
|
448
|
+
function warnOnce(reason, error) {
|
|
449
|
+
if (warnedReasons.has(reason)) return
|
|
450
|
+
warnedReasons.add(reason)
|
|
451
|
+
console.warn('[' + NS + '] ' + reason, error)
|
|
452
|
+
}
|
|
453
|
+
|
|
388
454
|
/**
|
|
389
455
|
* The host's message clock: `HH:mm` for today, the `clock.md` date plus
|
|
390
456
|
* the clock earlier this year, the `clock.ymd` form for other years.
|
|
@@ -394,12 +460,25 @@ window.__ModuleLoader__.load({
|
|
|
394
460
|
* @returns the display string.
|
|
395
461
|
*/
|
|
396
462
|
function formatMessageClock(time, t, now) {
|
|
397
|
-
|
|
398
|
-
|
|
463
|
+
// `new Date(x)` throws on a Symbol, a BigInt, or an object whose `valueOf`
|
|
464
|
+
// throws — and the host hands `data.time` through unchanged. A throw here
|
|
465
|
+
// happens INSIDE the seat's subtree, outside MarkdownBoundary (the strip is
|
|
466
|
+
// not the markdown body), so it would escape to the slot machinery and
|
|
467
|
+
// retire the whole entry. A missing clock is a cosmetic loss; a retired
|
|
468
|
+
// renderer is "the plugin stopped working".
|
|
469
|
+
let date
|
|
470
|
+
let reference
|
|
471
|
+
try {
|
|
472
|
+
date = new Date(time)
|
|
473
|
+
reference = new Date(now)
|
|
474
|
+
} catch (error) {
|
|
475
|
+
warnOnce('the message time cannot be read; the clock is hidden', error)
|
|
476
|
+
return ''
|
|
477
|
+
}
|
|
399
478
|
const clock = `${pad2(date.getHours())}:${pad2(date.getMinutes())}`
|
|
400
479
|
if (date.getFullYear() === reference.getFullYear() && date.getMonth() === reference.getMonth() && date.getDate() === reference.getDate()) return clock
|
|
401
480
|
const params = { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate() }
|
|
402
|
-
return `${date.getFullYear() === reference.getFullYear() ? t
|
|
481
|
+
return `${date.getFullYear() === reference.getFullYear() ? safeTranslate(t, 'clock.md', params) : safeTranslate(t, 'clock.ymd', params)} ${clock}`
|
|
403
482
|
}
|
|
404
483
|
|
|
405
484
|
// --- pieces -----------------------------------------------------------------
|
|
@@ -477,7 +556,7 @@ window.__ModuleLoader__.load({
|
|
|
477
556
|
|
|
478
557
|
const clock =
|
|
479
558
|
time === undefined ? null : h('span', { className: 'dshmb-timeStart' }, formatMessageClock(time, t, day))
|
|
480
|
-
const label = copied ? t
|
|
559
|
+
const label = copied ? safeTranslate(t, 'copied') : safeTranslate(t, 'copy')
|
|
481
560
|
|
|
482
561
|
return h(
|
|
483
562
|
'div',
|
|
@@ -501,22 +580,36 @@ window.__ModuleLoader__.load({
|
|
|
501
580
|
* @param props - the attachment entry and the owner's image renderer.
|
|
502
581
|
* @returns the row entry.
|
|
503
582
|
*/
|
|
504
|
-
function AttachmentEntry(
|
|
583
|
+
function AttachmentEntry(props) {
|
|
584
|
+
// The image branch calls the HOST's renderer during our render, and the
|
|
585
|
+
// file branch calls host primitives (`fileExtension`, `fileSizeText`). Both
|
|
586
|
+
// sit outside MarkdownBoundary, so either can retire the entry. One
|
|
587
|
+
// attachment is worth losing; the row is not.
|
|
588
|
+
try {
|
|
589
|
+
return renderAttachmentEntry(props)
|
|
590
|
+
} catch (error) {
|
|
591
|
+
warnOnce('an attachment could not be rendered; it is skipped', error)
|
|
592
|
+
return null
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function renderAttachmentEntry({ attachment, renderMessageImages, compactImages }) {
|
|
505
597
|
if (attachment.type === 'image') {
|
|
506
598
|
return h(Fragment, null, renderMessageImages({ images: [attachment.image], align: 'end', compact: compactImages }))
|
|
507
599
|
}
|
|
508
600
|
const file = attachment.file ?? {}
|
|
509
|
-
const
|
|
601
|
+
const name = typeof file.name === 'string' ? file.name : ''
|
|
602
|
+
const meta = [primitives.fileExtension(name).toUpperCase().slice(0, 8), primitives.fileSizeText(file.bytes)]
|
|
510
603
|
.filter(Boolean)
|
|
511
604
|
.join(' ')
|
|
512
605
|
return h(
|
|
513
606
|
'span',
|
|
514
|
-
{ className: 'dshmb-fileCard', title:
|
|
515
|
-
h(primitives.FileTypeIcon, { path:
|
|
607
|
+
{ className: 'dshmb-fileCard', title: name },
|
|
608
|
+
h(primitives.FileTypeIcon, { path: name, className: 'dshmb-fileIcon' }),
|
|
516
609
|
h(
|
|
517
610
|
'span',
|
|
518
611
|
{ className: 'dshmb-fileContent' },
|
|
519
|
-
h('span', { className: 'dshmb-fileName' },
|
|
612
|
+
h('span', { className: 'dshmb-fileName' }, name),
|
|
520
613
|
h('span', { className: 'dshmb-fileMeta' }, meta),
|
|
521
614
|
),
|
|
522
615
|
)
|
|
@@ -529,22 +622,46 @@ window.__ModuleLoader__.load({
|
|
|
529
622
|
* @param props - keyed chat renderer seat for the `user`/`steering` kinds.
|
|
530
623
|
* @returns the right-aligned bubble with attachments, markdown, chips and actions.
|
|
531
624
|
*/
|
|
532
|
-
const MarkdownBubbleSeat = react.memo(function MarkdownBubbleSeat({
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
625
|
+
const MarkdownBubbleSeat = react.memo(function MarkdownBubbleSeat(props) {
|
|
626
|
+
try {
|
|
627
|
+
return renderMarkdownBubble(props)
|
|
628
|
+
} catch (error) {
|
|
629
|
+
// Last-resort net. Everything that can throw is guarded where it is
|
|
630
|
+
// called (see safeMarkdownBody, plainProjection, formatMessageClock,
|
|
631
|
+
// AttachmentEntry); this exists so an UNKNOWN host shape degrades one
|
|
632
|
+
// row to plain text instead of retiring the renderer. The fallback still
|
|
633
|
+
// renders the action strip, because the three sibling plugins anchor
|
|
634
|
+
// their own buttons on `row.querySelector('[class*="_actions"]')`.
|
|
635
|
+
warnOnce('the markdown seat failed to render; showing the plain text', error)
|
|
636
|
+
return renderPlainRow(props)
|
|
637
|
+
}
|
|
638
|
+
})
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* The real seat body. Separated from the memo wrapper so the wrapper can hold
|
|
642
|
+
* one try/catch for the whole synchronous render.
|
|
643
|
+
* @param props - keyed chat renderer seat for the `user`/`steering` kinds.
|
|
644
|
+
* @returns the right-aligned bubble with attachments, markdown, chips and actions.
|
|
645
|
+
*/
|
|
646
|
+
function renderMarkdownBubble(props) {
|
|
647
|
+
const safeProps = props !== null && typeof props === 'object' ? props : {}
|
|
648
|
+
const node = safeProps.node !== null && typeof safeProps.node === 'object' ? safeProps.node : {}
|
|
649
|
+
const renderMessageImages = safeProps.renderMessageImages
|
|
650
|
+
const openFile = safeProps.openFile
|
|
651
|
+
const openSkill = safeProps.openSkill
|
|
652
|
+
const t = safeProps.t
|
|
653
|
+
const data = node.data !== null && typeof node.data === 'object' ? node.data : {}
|
|
654
|
+
const { text, attachments, rest } = contentParts(Array.isArray(data.content) ? data.content : [])
|
|
655
|
+
const referenceLabels = Array.isArray(data.referenceLabels) ? data.referenceLabels : []
|
|
656
|
+
const skillNames = Array.isArray(data.skillNames) ? data.skillNames : []
|
|
543
657
|
const references = react.useMemo(() => ({ openFile, openSkill }), [openFile, openSkill])
|
|
544
|
-
const labels = react.useMemo(() => buildMarkdownLabels(t), [t])
|
|
545
658
|
const compactImages = attachments.length > 1
|
|
546
659
|
const showBubble = text !== '' || rest.length > 0
|
|
547
660
|
|
|
661
|
+
// Never let composition throw out of this render — see safeMarkdownBody.
|
|
662
|
+
const body = safeMarkdownBody(text, t, referenceLabels, skillNames, references)
|
|
663
|
+
const fallback = plainProjection(text, referenceLabels, skillNames, references)
|
|
664
|
+
|
|
548
665
|
// The action strip leads the row in DOM order and is pushed below the
|
|
549
666
|
// bubble by flex `order`. Sibling plugins pick their bar with
|
|
550
667
|
// `row.querySelector('[class*="_actions"]')` — first match in document
|
|
@@ -576,17 +693,13 @@ window.__ModuleLoader__.load({
|
|
|
576
693
|
h(
|
|
577
694
|
'div',
|
|
578
695
|
{ className: 'dshmb-bubble' },
|
|
579
|
-
h(
|
|
580
|
-
MarkdownBoundary,
|
|
581
|
-
{ key: text, fallback: plainProjection(text, referenceLabels, skillNames, references) },
|
|
582
|
-
composeUserMarkdown(text, labels, referenceLabels, skillNames, references),
|
|
583
|
-
),
|
|
696
|
+
h(MarkdownBoundary, { key: text, fallback }, body === null ? fallback : body),
|
|
584
697
|
rest.map((block, index) =>
|
|
585
698
|
h(primitives.JsonBlock, {
|
|
586
699
|
key: 'rest' + index,
|
|
587
|
-
label: t
|
|
700
|
+
label: safeTranslate(t, 'message.extraBlock'),
|
|
588
701
|
payload: block,
|
|
589
|
-
truncatedLabel: (total) => t
|
|
702
|
+
truncatedLabel: (total) => safeTranslate(t, 'json.truncated', { total }),
|
|
590
703
|
}),
|
|
591
704
|
),
|
|
592
705
|
),
|
|
@@ -594,11 +707,39 @@ window.__ModuleLoader__.load({
|
|
|
594
707
|
h(
|
|
595
708
|
'div',
|
|
596
709
|
{ className: 'dshmb-referenceSummary' },
|
|
597
|
-
t
|
|
710
|
+
safeTranslate(t, 'message.referenceSummary', {
|
|
711
|
+
labels: referenceLabels.join(safeTranslate(t, 'message.referenceSeparator')),
|
|
712
|
+
}),
|
|
598
713
|
),
|
|
599
714
|
),
|
|
600
715
|
)
|
|
601
|
-
}
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* The degraded row: the action strip plus the raw text, built so that nothing
|
|
720
|
+
* in it can throw.
|
|
721
|
+
* @param props - whatever the seat was handed.
|
|
722
|
+
* @returns the row with its sibling anchors intact.
|
|
723
|
+
*/
|
|
724
|
+
function renderPlainRow(props) {
|
|
725
|
+
const safeProps = props !== null && typeof props === 'object' ? props : {}
|
|
726
|
+
const node = safeProps.node !== null && typeof safeProps.node === 'object' ? safeProps.node : {}
|
|
727
|
+
const data = node.data !== null && typeof node.data === 'object' ? node.data : {}
|
|
728
|
+
let text = ''
|
|
729
|
+
try {
|
|
730
|
+
text = contentParts(Array.isArray(data.content) ? data.content : []).text
|
|
731
|
+
} catch {
|
|
732
|
+
text = ''
|
|
733
|
+
}
|
|
734
|
+
return h(
|
|
735
|
+
'div',
|
|
736
|
+
{ className: 'dshmb-row', 'data-dshmb-row': '1', 'data-dshmb-degraded': '1' },
|
|
737
|
+
h(MessageActions, { text, time: data.time, t: safeProps.t }),
|
|
738
|
+
text === ''
|
|
739
|
+
? null
|
|
740
|
+
: h('div', { className: 'dshmb-stack' }, h('div', { className: 'dshmb-bubble' }, h('span', { className: 'dshmb-plain' }, text))),
|
|
741
|
+
)
|
|
742
|
+
}
|
|
602
743
|
|
|
603
744
|
// --- style ------------------------------------------------------------------
|
|
604
745
|
|
package/src/index.js
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
export const PLUGIN_ID = 'dsh-markdown-bubble'
|
|
16
16
|
|
|
17
17
|
/** Keep in sync with package.json and src/client.js. */
|
|
18
|
-
export const PLUGIN_VERSION = '0.1.
|
|
18
|
+
export const PLUGIN_VERSION = '0.1.3'
|
|
19
19
|
|
|
20
20
|
/** Cordis plugin name; the bundle patch resolves the package by this row. */
|
|
21
21
|
export const name = PLUGIN_ID
|