markstream-svelte 0.0.6 → 0.1.0-beta.2
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 +14 -17
- package/dist/components/CodeBlockNode.svelte +235 -266
- package/dist/components/CodeBlockNode.svelte.d.ts +6 -5
- package/dist/components/NodeOutlet.svelte +13 -2
- package/dist/components/NodeRenderer.svelte +16 -5
- package/dist/components/PreCodeNode.svelte +1 -1
- package/dist/components/shared/node-helpers.d.ts +14 -13
- package/dist/components/shared/node-helpers.js +1 -1
- package/dist/enhanceRenderedHtml.d.ts +5 -2
- package/dist/enhanceRenderedHtml.js +134 -40
- package/dist/index.css +10 -69
- package/dist/index.d.ts +3 -4
- package/dist/index.js +2 -3
- package/dist/index.px.css +10 -69
- package/dist/index.tailwind.css +10 -69
- package/dist/optional/{monaco.d.ts → streamDiffs.d.ts} +5 -6
- package/dist/optional/streamDiffs.js +66 -0
- package/dist/types/codeBlock.d.ts +66 -0
- package/dist/utils/languageIcon.d.ts +1 -1
- package/dist/utils/languageIcon.js +7 -5
- package/package.json +6 -10
- package/dist/components/MarkdownCodeBlockNode.svelte +0 -15
- package/dist/components/MarkdownCodeBlockNode.svelte.d.ts +0 -8
- package/dist/optional/monaco.js +0 -84
- package/dist/types/monaco.d.ts +0 -65
- /package/dist/types/{monaco.js → codeBlock.js} +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { CodeBlockOptions, CodeBlockTheme, CodeBlockThemeProp, CodeBlockThemes } from '../types/codeBlock'
|
|
3
3
|
import type { SvelteRenderableNode, SvelteRenderContext } from './shared/node-helpers'
|
|
4
4
|
import { onDestroy, onMount, tick } from 'svelte'
|
|
5
5
|
import { useSafeI18n } from '../i18n/useSafeI18n'
|
|
6
|
-
import {
|
|
6
|
+
import { getStreamDiffsRuntime } from '../optional/streamDiffs'
|
|
7
7
|
import { hideTooltip, showTooltipForAnchor } from '../tooltip/singletonTooltip'
|
|
8
|
-
import { getLanguageIcon, isLikelyIncompleteLanguageIdentifier, languageMap, normalizeLanguageIdentifier,
|
|
8
|
+
import { getLanguageIcon, isLikelyIncompleteLanguageIdentifier, languageMap, normalizeLanguageIdentifier, resolveLanguageId } from '../utils/languageIcon'
|
|
9
9
|
import HtmlPreviewFrame from './HtmlPreviewFrame.svelte'
|
|
10
10
|
import PreCodeNode from './PreCodeNode.svelte'
|
|
11
11
|
import { copyTextToClipboard, resolveCssSize } from './shared/rich-block-helpers'
|
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
isDark?: boolean | undefined
|
|
18
18
|
loading?: boolean | undefined
|
|
19
19
|
stream?: boolean | undefined
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
codeBlockOptions?: CodeBlockOptions | undefined
|
|
21
|
+
theme?: CodeBlockThemeProp | undefined
|
|
22
|
+
darkTheme?: CodeBlockTheme | undefined
|
|
23
|
+
lightTheme?: CodeBlockTheme | undefined
|
|
24
|
+
themes?: CodeBlockThemes | undefined
|
|
24
25
|
minWidth?: string | number | undefined
|
|
25
26
|
maxWidth?: string | number | undefined
|
|
26
27
|
isShowPreview?: boolean
|
|
@@ -42,10 +43,11 @@
|
|
|
42
43
|
isDark = undefined,
|
|
43
44
|
loading = undefined,
|
|
44
45
|
stream = undefined,
|
|
46
|
+
codeBlockOptions = undefined,
|
|
47
|
+
theme = undefined,
|
|
45
48
|
darkTheme = undefined,
|
|
46
49
|
lightTheme = undefined,
|
|
47
50
|
themes = undefined,
|
|
48
|
-
monacoOptions = undefined,
|
|
49
51
|
minWidth = undefined,
|
|
50
52
|
maxWidth = undefined,
|
|
51
53
|
isShowPreview = true,
|
|
@@ -56,24 +58,12 @@
|
|
|
56
58
|
showPreviewButton = true,
|
|
57
59
|
showCollapseButton = true,
|
|
58
60
|
showFontSizeButtons = true,
|
|
59
|
-
showLineNumbers =
|
|
61
|
+
showLineNumbers = undefined,
|
|
60
62
|
htmlPreviewAllowScripts = false,
|
|
61
63
|
htmlPreviewSandbox = undefined
|
|
62
64
|
}: Props = $props()
|
|
63
65
|
|
|
64
66
|
const { t } = useSafeI18n()
|
|
65
|
-
const defaultDiffHideUnchangedRegions = Object.freeze({
|
|
66
|
-
enabled: true,
|
|
67
|
-
contextLineCount: 2,
|
|
68
|
-
minimumLineCount: 4,
|
|
69
|
-
revealLineCount: 5,
|
|
70
|
-
})
|
|
71
|
-
const disabledDiffHideUnchangedRegions = Object.freeze({
|
|
72
|
-
enabled: false,
|
|
73
|
-
contextLineCount: 0,
|
|
74
|
-
minimumLineCount: Number.POSITIVE_INFINITY,
|
|
75
|
-
revealLineCount: 0,
|
|
76
|
-
})
|
|
77
67
|
const streamingLanguageTokens = ['javascript', 'plaintext', 'shellscript', 'typescript']
|
|
78
68
|
const defaultPreFallbackFontFamily = '"SF Mono", Monaco, Consolas, "Ubuntu Mono", "Liberation Mono", "Courier New", monospace'
|
|
79
69
|
|
|
@@ -127,8 +117,8 @@
|
|
|
127
117
|
|
|
128
118
|
let editorHost: HTMLDivElement | null = $state(null)
|
|
129
119
|
let helpers: any = $state(null)
|
|
130
|
-
let
|
|
131
|
-
let
|
|
120
|
+
let runtimeOptions: Record<string, any> | null = $state(null)
|
|
121
|
+
let ensureRuntimePromise: Promise<void> | null = $state(null)
|
|
132
122
|
let editorReady = $state(false)
|
|
133
123
|
let useFallback = $state(false)
|
|
134
124
|
let fallbackLanguage = $state('')
|
|
@@ -150,6 +140,7 @@
|
|
|
150
140
|
let lastLayoutWidth: number | null = $state(null)
|
|
151
141
|
let lastLayoutHeight: number | null = $state(null)
|
|
152
142
|
let lastThemeRequest = $state('')
|
|
143
|
+
let lastRuntimeInstallationConfig: unknown
|
|
153
144
|
let languageRetryTimer: ReturnType<typeof setTimeout> | null = $state(null)
|
|
154
145
|
let loadingSettledRefreshPromise: Promise<void> | null = $state(null)
|
|
155
146
|
let loadingSettledRefreshTimer: ReturnType<typeof setTimeout> | null = $state(null)
|
|
@@ -160,7 +151,7 @@
|
|
|
160
151
|
|
|
161
152
|
let rawLanguage = $derived(getString((node as any)?.language).trim())
|
|
162
153
|
let canonicalLanguage = $derived(normalizeLanguageIdentifier(rawLanguage))
|
|
163
|
-
let
|
|
154
|
+
let runtimeLanguage = $derived(resolveLanguageId(canonicalLanguage || rawLanguage || 'plaintext'))
|
|
164
155
|
let code = $derived(getResolvedCode(node))
|
|
165
156
|
let diff = $derived(Boolean((node as any)?.diff))
|
|
166
157
|
let originalCode = $derived(getString((node as any)?.originalCode))
|
|
@@ -170,15 +161,21 @@
|
|
|
170
161
|
let resolvedStream = $derived(stream ?? context?.codeBlockStream ?? true)
|
|
171
162
|
let resolvedIsDark = $derived(isDark ?? context?.isDark ?? false)
|
|
172
163
|
let resolvedThemes = $derived(context?.codeBlockThemes)
|
|
173
|
-
let
|
|
174
|
-
let
|
|
175
|
-
let
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
164
|
+
let resolvedCodeBlockOptions = $derived(codeBlockOptions ?? context?.codeBlockOptions)
|
|
165
|
+
let effectiveShowLineNumbers = $derived(showLineNumbers ?? resolvedCodeBlockOptions?.disableLineNumbers !== true)
|
|
166
|
+
let runtimeInstallationConfig = $derived.by(() => {
|
|
167
|
+
const parseDiffOptions = resolvedCodeBlockOptions?.parseDiffOptions
|
|
168
|
+
return {
|
|
169
|
+
options: { ...(resolvedCodeBlockOptions ?? {}) },
|
|
170
|
+
parseDiffOptions: parseDiffOptions && typeof parseDiffOptions === 'object'
|
|
171
|
+
? { ...parseDiffOptions }
|
|
172
|
+
: parseDiffOptions,
|
|
173
|
+
showLineNumbers: effectiveShowLineNumbers,
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
let resolvedRuntimeOptions = $derived(buildResolvedRuntimeOptions())
|
|
177
|
+
let requestedTheme = $derived(resolveRequestedTheme())
|
|
178
|
+
let defaultCodeFontSize = $derived(readPositiveMetric(resolvedCodeBlockOptions?.fontSize) ?? 12)
|
|
182
179
|
let minWidthValue = $derived(resolveCssSize(minWidth ?? resolvedThemes?.minWidth))
|
|
183
180
|
let maxWidthValue = $derived(resolveCssSize(maxWidth ?? resolvedThemes?.maxWidth))
|
|
184
181
|
let containerStyle = $derived([
|
|
@@ -200,8 +197,8 @@
|
|
|
200
197
|
} as SvelteRenderableNode)
|
|
201
198
|
let preFallbackStyle = $derived(buildPreFallbackStyle())
|
|
202
199
|
let settledRefreshSignature = $derived(diff
|
|
203
|
-
? `${
|
|
204
|
-
: `${
|
|
200
|
+
? `${runtimeLanguage}\0${originalCode}\0${updatedCode || code}`
|
|
201
|
+
: `${runtimeLanguage}\0${code}`)
|
|
205
202
|
|
|
206
203
|
$effect(() => {
|
|
207
204
|
if (useFallback && fallbackLanguage && rawLanguage !== fallbackLanguage && isLikelyIncompleteLanguageIdentifier(fallbackLanguage)) {
|
|
@@ -235,13 +232,21 @@
|
|
|
235
232
|
void code
|
|
236
233
|
void originalCode
|
|
237
234
|
void updatedCode
|
|
238
|
-
void
|
|
235
|
+
void runtimeLanguage
|
|
239
236
|
void requestedTheme
|
|
240
|
-
void
|
|
237
|
+
void resolvedRuntimeOptions
|
|
238
|
+
void resolvedCodeBlockOptions
|
|
239
|
+
void runtimeInstallationConfig
|
|
241
240
|
void codeFontSize
|
|
242
241
|
void expanded
|
|
243
|
-
if (mounted)
|
|
242
|
+
if (mounted) {
|
|
243
|
+
if (lastRuntimeInstallationConfig !== runtimeInstallationConfig) {
|
|
244
|
+
lastRuntimeInstallationConfig = runtimeInstallationConfig
|
|
245
|
+
codeFontSize = defaultCodeFontSize
|
|
246
|
+
cleanupEditor()
|
|
247
|
+
}
|
|
244
248
|
void syncEditor()
|
|
249
|
+
}
|
|
245
250
|
})
|
|
246
251
|
|
|
247
252
|
onMount(() => {
|
|
@@ -279,29 +284,30 @@
|
|
|
279
284
|
}
|
|
280
285
|
|
|
281
286
|
function getCodeLineHeight() {
|
|
282
|
-
return readPositiveMetric(
|
|
287
|
+
return readPositiveMetric(resolvedCodeBlockOptions?.lineHeight)
|
|
283
288
|
?? (codeFontSize === 12 ? 18 : Math.max(12, Math.round(codeFontSize * 1.5)))
|
|
284
289
|
}
|
|
285
290
|
|
|
286
291
|
function getCodePadding() {
|
|
287
|
-
const padding =
|
|
292
|
+
const padding = resolvedCodeBlockOptions?.padding
|
|
288
293
|
const defaultPadding = diff ? 0 : 8
|
|
294
|
+
const value = readNonNegativeMetric(padding) ?? defaultPadding
|
|
289
295
|
return {
|
|
290
|
-
top:
|
|
291
|
-
bottom:
|
|
296
|
+
top: value,
|
|
297
|
+
bottom: value,
|
|
292
298
|
}
|
|
293
299
|
}
|
|
294
300
|
|
|
295
301
|
function getCodeFontFamily() {
|
|
296
|
-
return typeof
|
|
297
|
-
?
|
|
302
|
+
return typeof resolvedCodeBlockOptions?.fontFamily === 'string' && resolvedCodeBlockOptions.fontFamily.trim()
|
|
303
|
+
? resolvedCodeBlockOptions.fontFamily.trim()
|
|
298
304
|
: defaultPreFallbackFontFamily
|
|
299
305
|
}
|
|
300
306
|
|
|
301
307
|
function buildPreFallbackStyle() {
|
|
302
308
|
const padding = getCodePadding()
|
|
303
309
|
const fontFamily = getCodeFontFamily()
|
|
304
|
-
const tabSize = readPositiveMetric(
|
|
310
|
+
const tabSize = readPositiveMetric(resolvedCodeBlockOptions?.tabSize) ?? 4
|
|
305
311
|
const lineHeight = getCodeLineHeight()
|
|
306
312
|
return [
|
|
307
313
|
`--markstream-code-font-family: ${fontFamily}`,
|
|
@@ -317,181 +323,145 @@
|
|
|
317
323
|
`padding-bottom: ${padding.bottom}px`,
|
|
318
324
|
`padding-left: var(--markstream-code-padding-left, 52px)`,
|
|
319
325
|
`tab-size: ${tabSize}`,
|
|
326
|
+
`max-height: ${getMaxHeightValue()}px`,
|
|
327
|
+
'overflow: auto',
|
|
328
|
+
`white-space: ${resolvedCodeBlockOptions?.overflow === 'scroll' ? 'pre' : 'pre-wrap'}`,
|
|
320
329
|
].join('; ')
|
|
321
330
|
}
|
|
322
331
|
|
|
323
|
-
function
|
|
332
|
+
function isThemePair(value: unknown): value is { dark: string, light: string } {
|
|
333
|
+
return !!value && typeof value === 'object' && typeof (value as any).dark === 'string' && typeof (value as any).light === 'string'
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function resolveRequestedTheme() {
|
|
324
337
|
if (typeof theme === 'string' && theme)
|
|
325
338
|
return theme
|
|
326
|
-
if (
|
|
327
|
-
return
|
|
328
|
-
|
|
339
|
+
if (isThemePair(theme))
|
|
340
|
+
return resolvedIsDark ? theme.dark : theme.light
|
|
341
|
+
const directTheme = resolvedIsDark ? darkTheme : lightTheme
|
|
342
|
+
if (directTheme)
|
|
343
|
+
return directTheme
|
|
344
|
+
if (themes)
|
|
345
|
+
return resolvedIsDark ? themes[0] : themes[1]
|
|
346
|
+
const contextTheme = resolvedIsDark ? resolvedThemes?.darkTheme : resolvedThemes?.lightTheme
|
|
347
|
+
if (contextTheme)
|
|
348
|
+
return contextTheme
|
|
349
|
+
if (resolvedThemes?.themes)
|
|
350
|
+
return resolvedIsDark ? resolvedThemes.themes[0] : resolvedThemes.themes[1]
|
|
351
|
+
return resolvedIsDark ? 'vitesse-dark' : 'vitesse-light'
|
|
329
352
|
}
|
|
330
353
|
|
|
331
|
-
function buildThemeList() {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
}
|
|
337
|
-
add(darkTheme ?? resolvedThemes?.darkTheme)
|
|
338
|
-
add(lightTheme ?? resolvedThemes?.lightTheme)
|
|
339
|
-
for (const item of resolvedThemes?.themes || [])
|
|
340
|
-
add(item)
|
|
341
|
-
for (const item of themes || [])
|
|
342
|
-
add(item)
|
|
343
|
-
return list
|
|
354
|
+
function buildThemeList(): [dark: string, light: string] {
|
|
355
|
+
return [
|
|
356
|
+
darkTheme ?? themes?.[0] ?? resolvedThemes?.darkTheme ?? resolvedThemes?.themes?.[0] ?? 'vitesse-dark',
|
|
357
|
+
lightTheme ?? themes?.[1] ?? resolvedThemes?.lightTheme ?? resolvedThemes?.themes?.[1] ?? 'vitesse-light',
|
|
358
|
+
]
|
|
344
359
|
}
|
|
345
360
|
|
|
346
|
-
function
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
361
|
+
function buildResolvedRuntimeOptions() {
|
|
362
|
+
const userOptions = { ...(resolvedCodeBlockOptions ?? {}) } as Record<string, any>
|
|
363
|
+
for (const key of [
|
|
364
|
+
'maxHeight',
|
|
365
|
+
'padding',
|
|
366
|
+
'tabSize',
|
|
367
|
+
'theme',
|
|
368
|
+
'themes',
|
|
369
|
+
'themeType',
|
|
370
|
+
'language',
|
|
371
|
+
'languages',
|
|
372
|
+
'stream',
|
|
373
|
+
'disableFileHeader',
|
|
374
|
+
'onThemeChange',
|
|
375
|
+
'renderCustomHeader',
|
|
376
|
+
'renderHeaderMetadata',
|
|
377
|
+
'renderHeaderPrefix',
|
|
378
|
+
])
|
|
379
|
+
delete userOptions[key]
|
|
380
|
+
|
|
381
|
+
const parseDiffOptions = userOptions.parseDiffOptions && typeof userOptions.parseDiffOptions === 'object'
|
|
382
|
+
? userOptions.parseDiffOptions as Record<string, unknown>
|
|
383
|
+
: {}
|
|
384
|
+
const nativeOptions = diff
|
|
385
|
+
? {
|
|
386
|
+
diffStyle: 'split',
|
|
387
|
+
expandUnchanged: false,
|
|
388
|
+
collapsedContextThreshold: 5,
|
|
389
|
+
hunkSeparators: 'line-info',
|
|
390
|
+
...userOptions,
|
|
391
|
+
parseDiffOptions: {
|
|
392
|
+
context: 2,
|
|
393
|
+
...parseDiffOptions,
|
|
394
|
+
},
|
|
395
|
+
}
|
|
396
|
+
: userOptions
|
|
397
|
+
const configuredUnsafeCSS = typeof nativeOptions.unsafeCSS === 'string' ? nativeOptions.unsafeCSS : ''
|
|
359
398
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
readOnly: true,
|
|
365
|
-
minimap: { enabled: false },
|
|
366
|
-
lineNumbers: 'on',
|
|
367
|
-
wordWrap: 'on',
|
|
368
|
-
wrappingIndent: 'same',
|
|
369
|
-
revealDebounceMs: 75,
|
|
370
|
-
}
|
|
371
|
-
const padding = getCodePadding()
|
|
372
|
-
const configuredUnsafeCSS = typeof raw.unsafeCSS === 'string' ? raw.unsafeCSS : ''
|
|
373
|
-
const unsafeCSS = `[data-file], [data-diff] { --diffs-min-number-column-width-default: 4ch !important; }
|
|
374
|
-
${configuredUnsafeCSS}`.trim()
|
|
375
|
-
const finalOptions = {
|
|
376
|
-
MAX_HEIGHT: maxHeight,
|
|
399
|
+
return {
|
|
400
|
+
overflow: 'wrap',
|
|
401
|
+
...nativeOptions,
|
|
402
|
+
MAX_HEIGHT: expanded ? 900 : (resolvedCodeBlockOptions?.maxHeight ?? 500),
|
|
377
403
|
fontFamily: getCodeFontFamily(),
|
|
378
404
|
fontSize: codeFontSize,
|
|
379
405
|
lineHeight: getCodeLineHeight(),
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
padding,
|
|
384
|
-
unsafeCSS,
|
|
385
|
-
// The component owns the streaming fallback and file header. Initialize
|
|
386
|
-
// stream-diffs in final mode so the revealed surface has highlighting,
|
|
387
|
-
// line numbers, and the same geometry as the fallback.
|
|
406
|
+
disableLineNumbers: !effectiveShowLineNumbers,
|
|
407
|
+
unsafeCSS: `[data-file], [data-diff] { --diffs-min-number-column-width-default: 2ch !important; }
|
|
408
|
+
${configuredUnsafeCSS}`.trim(),
|
|
388
409
|
disableFileHeader: true,
|
|
389
410
|
stream: false,
|
|
390
411
|
themes: buildThemeList(),
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
...raw,
|
|
397
|
-
...finalOptions,
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const diffHideUnchangedRegions = raw.diffHideUnchangedRegions === undefined
|
|
402
|
-
? { ...defaultDiffHideUnchangedRegions }
|
|
403
|
-
: resolveDiffHideUnchangedRegionsOption(raw.diffHideUnchangedRegions)
|
|
404
|
-
const hideUnchangedRegions = raw.hideUnchangedRegions === undefined
|
|
405
|
-
? undefined
|
|
406
|
-
: resolveDiffHideUnchangedRegionsOption(raw.hideUnchangedRegions)
|
|
407
|
-
const streamPreviewDiff = resolvedStream !== false && resolvedLoading !== false
|
|
408
|
-
const activeDiffHideUnchangedRegions = streamPreviewDiff
|
|
409
|
-
? { ...disabledDiffHideUnchangedRegions }
|
|
410
|
-
: diffHideUnchangedRegions
|
|
411
|
-
const activeHideUnchangedRegions = streamPreviewDiff
|
|
412
|
-
? { ...disabledDiffHideUnchangedRegions }
|
|
413
|
-
: hideUnchangedRegions
|
|
414
|
-
const experimental = {
|
|
415
|
-
...((raw.experimental as Record<string, unknown> | undefined) ?? {}),
|
|
416
|
-
}
|
|
417
|
-
const diffUnchangedRegionStyle = raw.diffUnchangedRegionStyle ?? 'line-info'
|
|
418
|
-
const diffDefaults = {
|
|
419
|
-
maxComputationTime: 0,
|
|
420
|
-
diffAlgorithm: 'legacy',
|
|
421
|
-
ignoreTrimWhitespace: false,
|
|
422
|
-
renderIndicators: true,
|
|
423
|
-
diffUpdateThrottleMs: 120,
|
|
424
|
-
renderLineHighlight: 'none',
|
|
425
|
-
renderLineHighlightOnlyWhenFocus: true,
|
|
426
|
-
selectionHighlight: false,
|
|
427
|
-
occurrencesHighlight: 'off',
|
|
428
|
-
matchBrackets: 'never',
|
|
429
|
-
lineDecorationsWidth: 4,
|
|
430
|
-
lineNumbersMinChars: 2,
|
|
431
|
-
glyphMargin: false,
|
|
432
|
-
renderOverviewRuler: false,
|
|
433
|
-
overviewRulerBorder: false,
|
|
434
|
-
hideCursorInOverviewRuler: true,
|
|
435
|
-
scrollBeyondLastLine: false,
|
|
436
|
-
diffHideUnchangedRegions: activeDiffHideUnchangedRegions,
|
|
437
|
-
useInlineViewWhenSpaceIsLimited: raw.useInlineViewWhenSpaceIsLimited ?? false,
|
|
438
|
-
diffLineStyle: 'background',
|
|
439
|
-
diffAppearance: 'auto',
|
|
440
|
-
diffUnchangedRegionStyle,
|
|
441
|
-
diffHunkActionsOnHover: false,
|
|
442
|
-
experimental,
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
return {
|
|
446
|
-
...baseOptions,
|
|
447
|
-
...diffDefaults,
|
|
448
|
-
...raw,
|
|
449
|
-
experimental,
|
|
450
|
-
...(activeHideUnchangedRegions === undefined ? {} : { hideUnchangedRegions: activeHideUnchangedRegions }),
|
|
451
|
-
diffHideUnchangedRegions: activeDiffHideUnchangedRegions,
|
|
452
|
-
...finalOptions,
|
|
412
|
+
themeType: resolvedIsDark ? 'dark' : 'light',
|
|
413
|
+
onThemeChange() {
|
|
414
|
+
syncEditorGeometryVars()
|
|
415
|
+
scheduleEditorHeightSync()
|
|
416
|
+
},
|
|
453
417
|
}
|
|
454
418
|
}
|
|
455
419
|
|
|
456
|
-
function
|
|
420
|
+
function syncRuntimeOptions() {
|
|
457
421
|
const nextOptions = {
|
|
458
|
-
...
|
|
422
|
+
...resolvedRuntimeOptions,
|
|
459
423
|
theme: requestedTheme,
|
|
460
424
|
}
|
|
461
|
-
if (!
|
|
462
|
-
|
|
463
|
-
return
|
|
425
|
+
if (!runtimeOptions) {
|
|
426
|
+
runtimeOptions = nextOptions
|
|
427
|
+
return runtimeOptions
|
|
464
428
|
}
|
|
465
|
-
for (const key of Object.keys(
|
|
429
|
+
for (const key of Object.keys(runtimeOptions)) {
|
|
466
430
|
if (!(key in nextOptions))
|
|
467
|
-
delete
|
|
431
|
+
delete runtimeOptions[key]
|
|
468
432
|
}
|
|
469
|
-
Object.assign(
|
|
470
|
-
return
|
|
433
|
+
Object.assign(runtimeOptions, nextOptions)
|
|
434
|
+
return runtimeOptions
|
|
471
435
|
}
|
|
472
436
|
|
|
473
|
-
async function
|
|
437
|
+
async function ensureRuntime() {
|
|
474
438
|
if (helpers || useFallback || typeof window === 'undefined')
|
|
475
439
|
return
|
|
476
|
-
if (
|
|
477
|
-
return
|
|
440
|
+
if (ensureRuntimePromise)
|
|
441
|
+
return ensureRuntimePromise
|
|
478
442
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
443
|
+
const runtimeId = lifecycleId
|
|
444
|
+
const pending = (async () => {
|
|
445
|
+
const mod = await getStreamDiffsRuntime()
|
|
446
|
+
if (!mounted || lifecycleId !== runtimeId)
|
|
482
447
|
return
|
|
483
|
-
if (!mod || typeof mod.
|
|
448
|
+
if (!mod || typeof mod.createCodeBlockRuntime !== 'function') {
|
|
484
449
|
useFallback = true
|
|
485
450
|
return
|
|
486
451
|
}
|
|
487
452
|
|
|
488
|
-
helpers = mod.
|
|
453
|
+
helpers = mod.createCodeBlockRuntime(syncRuntimeOptions())
|
|
489
454
|
await Promise.resolve(helpers.setTheme?.(requestedTheme))
|
|
455
|
+
if (!mounted || lifecycleId !== runtimeId)
|
|
456
|
+
return
|
|
490
457
|
lastThemeRequest = requestedTheme
|
|
491
|
-
})()
|
|
492
|
-
|
|
458
|
+
})()
|
|
459
|
+
const tracked = pending.finally(() => {
|
|
460
|
+
if (ensureRuntimePromise === tracked)
|
|
461
|
+
ensureRuntimePromise = null
|
|
493
462
|
})
|
|
494
|
-
|
|
463
|
+
ensureRuntimePromise = tracked
|
|
464
|
+
return tracked
|
|
495
465
|
}
|
|
496
466
|
|
|
497
467
|
function queueThemeSync() {
|
|
@@ -500,7 +470,7 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
500
470
|
lastThemeRequest = requestedTheme
|
|
501
471
|
void Promise.resolve(helpers.setTheme?.(requestedTheme)).catch((error) => {
|
|
502
472
|
if (typeof console !== 'undefined')
|
|
503
|
-
console.warn('[markstream-svelte] Failed to apply
|
|
473
|
+
console.warn('[markstream-svelte] Failed to apply code-block theme:', error)
|
|
504
474
|
})
|
|
505
475
|
}
|
|
506
476
|
|
|
@@ -508,10 +478,18 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
508
478
|
if (!mounted || !shouldRender || !editorHost || collapsed || shouldDelayEditor || shouldDeferStreamingLanguage)
|
|
509
479
|
return
|
|
510
480
|
|
|
511
|
-
|
|
512
|
-
|
|
481
|
+
const runtimeId = lifecycleId
|
|
482
|
+
try {
|
|
483
|
+
await ensureRuntime()
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
if (mounted && lifecycleId === runtimeId)
|
|
487
|
+
markEditorFallback(error)
|
|
488
|
+
return
|
|
489
|
+
}
|
|
490
|
+
if (!mounted || lifecycleId !== runtimeId || useFallback || !helpers)
|
|
513
491
|
return
|
|
514
|
-
|
|
492
|
+
syncRuntimeOptions()
|
|
515
493
|
|
|
516
494
|
const desiredKind: 'single' | 'diff' = diff ? 'diff' : 'single'
|
|
517
495
|
const hasEditorView = desiredKind === 'diff'
|
|
@@ -528,45 +506,43 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
528
506
|
return
|
|
529
507
|
}
|
|
530
508
|
|
|
509
|
+
const operationId = lifecycleId
|
|
531
510
|
try {
|
|
532
511
|
if (diff && typeof helpers.updateDiff === 'function')
|
|
533
|
-
await Promise.resolve(helpers.updateDiff(originalCode, updatedCode || code,
|
|
512
|
+
await Promise.resolve(helpers.updateDiff(originalCode, updatedCode || code, runtimeLanguage))
|
|
534
513
|
else if (typeof helpers.updateCode === 'function') {
|
|
535
|
-
await Promise.resolve(helpers.updateCode(code,
|
|
514
|
+
await Promise.resolve(helpers.updateCode(code, runtimeLanguage))
|
|
536
515
|
scheduleEditorTokenization()
|
|
537
516
|
}
|
|
517
|
+
if (!editorReady && await prepareEditorHandoff(desiredKind, lifecycleId)) {
|
|
518
|
+
editorRevealed = true
|
|
519
|
+
fallbackRetired = true
|
|
520
|
+
editorReady = true
|
|
521
|
+
}
|
|
538
522
|
queueThemeSync()
|
|
539
523
|
applyEditorOptions()
|
|
540
524
|
scheduleEditorHeightSync()
|
|
541
525
|
}
|
|
542
526
|
catch (error) {
|
|
543
|
-
|
|
527
|
+
if (mounted && lifecycleId === operationId)
|
|
528
|
+
markEditorFallback(error)
|
|
544
529
|
}
|
|
545
530
|
}
|
|
546
531
|
|
|
547
532
|
function hasRenderedEditorDom(kind: 'single' | 'diff') {
|
|
548
533
|
if (!editorHost)
|
|
549
534
|
return false
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
'.monaco-diff-editor',
|
|
553
|
-
'diffs-container',
|
|
554
|
-
'.stream-diffs-shell',
|
|
555
|
-
'[data-stream-diffs-state]',
|
|
556
|
-
].join(',')))
|
|
557
|
-
}
|
|
558
|
-
return Boolean(editorHost.querySelector([
|
|
559
|
-
'.monaco-editor',
|
|
535
|
+
// stream-diffs renders its surface inside these containers.
|
|
536
|
+
const selectors = [
|
|
560
537
|
'diffs-container',
|
|
561
538
|
'.stream-diffs-shell',
|
|
562
539
|
'[data-stream-diffs-state]',
|
|
563
|
-
].join(',')
|
|
540
|
+
].join(',')
|
|
541
|
+
return Boolean(editorHost.querySelector(selectors))
|
|
564
542
|
}
|
|
565
543
|
|
|
566
544
|
function getVisualEditorSurface() {
|
|
567
545
|
return editorHost?.querySelector<HTMLElement>([
|
|
568
|
-
'.monaco-diff-editor',
|
|
569
|
-
'.monaco-editor',
|
|
570
546
|
'diffs-container',
|
|
571
547
|
'[data-stream-diffs-state]',
|
|
572
548
|
'.stream-diffs-shell',
|
|
@@ -595,9 +571,8 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
595
571
|
|
|
596
572
|
async function prepareEditorHandoff(kind: 'single' | 'diff', creationId: number) {
|
|
597
573
|
await tick()
|
|
598
|
-
//
|
|
599
|
-
//
|
|
600
|
-
// mounted so the block never strands in the pre-fallback forever.
|
|
574
|
+
// Streaming updates retry this gate, so keep the fallback until the live
|
|
575
|
+
// surface has positive geometry.
|
|
601
576
|
const deadline = Date.now() + 1500
|
|
602
577
|
let attempt = 0
|
|
603
578
|
while (Date.now() < deadline && attempt < 30) {
|
|
@@ -612,33 +587,42 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
612
587
|
return isEditorVisuallyReady(kind)
|
|
613
588
|
}
|
|
614
589
|
}
|
|
615
|
-
return !!(mounted && editorHost && lifecycleId === creationId &&
|
|
590
|
+
return !!(mounted && editorHost && lifecycleId === creationId && isEditorVisuallyReady(kind))
|
|
616
591
|
}
|
|
617
592
|
|
|
618
593
|
async function recreateEditor(kind: 'single' | 'diff') {
|
|
619
594
|
if (!editorHost || !helpers || createEditorPromise)
|
|
620
595
|
return createEditorPromise
|
|
621
596
|
|
|
597
|
+
const activeHelpers = helpers
|
|
622
598
|
const creationId = ++lifecycleId
|
|
623
599
|
editorReady = false
|
|
624
|
-
|
|
600
|
+
const pending = (async () => {
|
|
625
601
|
try {
|
|
626
602
|
cleanupEditor(false)
|
|
627
|
-
if (!mounted || !editorHost || lifecycleId !== creationId)
|
|
603
|
+
if (!mounted || !editorHost || lifecycleId !== creationId || helpers !== activeHelpers)
|
|
628
604
|
return
|
|
629
605
|
editorHost.replaceChildren()
|
|
630
606
|
lastLayoutWidth = null
|
|
631
607
|
lastLayoutHeight = null
|
|
632
608
|
|
|
633
609
|
editorStreamMode = false
|
|
634
|
-
if (kind === 'diff' && typeof
|
|
635
|
-
await
|
|
636
|
-
|
|
610
|
+
if (kind === 'diff' && typeof activeHelpers.createDiffEditor === 'function') {
|
|
611
|
+
await activeHelpers.createDiffEditor(editorHost, originalCode, updatedCode || code, runtimeLanguage)
|
|
612
|
+
if (!mounted || lifecycleId !== creationId || helpers !== activeHelpers)
|
|
613
|
+
return
|
|
614
|
+
await Promise.resolve(activeHelpers.updateDiff?.(originalCode, updatedCode || code, runtimeLanguage))
|
|
615
|
+
if (!mounted || lifecycleId !== creationId || helpers !== activeHelpers)
|
|
616
|
+
return
|
|
637
617
|
editorKind = 'diff'
|
|
638
618
|
}
|
|
639
619
|
else {
|
|
640
|
-
await
|
|
641
|
-
|
|
620
|
+
await activeHelpers.createEditor(editorHost, code, runtimeLanguage)
|
|
621
|
+
if (!mounted || lifecycleId !== creationId || helpers !== activeHelpers)
|
|
622
|
+
return
|
|
623
|
+
await Promise.resolve(activeHelpers.updateCode?.(code, runtimeLanguage))
|
|
624
|
+
if (!mounted || lifecycleId !== creationId || helpers !== activeHelpers)
|
|
625
|
+
return
|
|
642
626
|
editorKind = 'single'
|
|
643
627
|
}
|
|
644
628
|
applyEditorOptions()
|
|
@@ -655,15 +639,17 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
655
639
|
scheduleEditorHeightSync()
|
|
656
640
|
}
|
|
657
641
|
catch (error) {
|
|
658
|
-
if (mounted) {
|
|
642
|
+
if (mounted && lifecycleId === creationId && helpers === activeHelpers) {
|
|
659
643
|
markEditorFallback(error)
|
|
660
644
|
}
|
|
661
645
|
}
|
|
662
|
-
})()
|
|
663
|
-
|
|
646
|
+
})()
|
|
647
|
+
const tracked = pending.finally(() => {
|
|
648
|
+
if (createEditorPromise === tracked)
|
|
649
|
+
createEditorPromise = null
|
|
664
650
|
})
|
|
665
|
-
|
|
666
|
-
return
|
|
651
|
+
createEditorPromise = tracked
|
|
652
|
+
return tracked
|
|
667
653
|
}
|
|
668
654
|
|
|
669
655
|
function refreshEditorAfterLoadingSettled() {
|
|
@@ -681,23 +667,23 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
681
667
|
}
|
|
682
668
|
if (!mounted || !shouldRender || !editorHost || collapsed || shouldDelayEditor || shouldDeferStreamingLanguage)
|
|
683
669
|
return
|
|
684
|
-
await
|
|
670
|
+
await ensureRuntime()
|
|
685
671
|
if (!mounted || useFallback || !helpers)
|
|
686
672
|
return
|
|
687
|
-
|
|
673
|
+
syncRuntimeOptions()
|
|
688
674
|
const desiredKind: 'single' | 'diff' = diff ? 'diff' : 'single'
|
|
689
675
|
if (!hasRenderedEditorDom(desiredKind) || editorKind !== desiredKind)
|
|
690
676
|
await recreateEditor(desiredKind)
|
|
691
677
|
if (!mounted || useFallback || !helpers || !hasRenderedEditorDom(desiredKind) || editorKind !== desiredKind)
|
|
692
678
|
return
|
|
693
679
|
if (diff) {
|
|
694
|
-
await Promise.resolve(helpers.updateDiff?.(originalCode, updatedCode || code,
|
|
680
|
+
await Promise.resolve(helpers.updateDiff?.(originalCode, updatedCode || code, runtimeLanguage))
|
|
695
681
|
helpers.refreshDiffPresentation?.()
|
|
696
682
|
applyEditorOptions()
|
|
697
683
|
scheduleEditorHeightSync()
|
|
698
684
|
return
|
|
699
685
|
}
|
|
700
|
-
await Promise.resolve(helpers.updateCode?.(code,
|
|
686
|
+
await Promise.resolve(helpers.updateCode?.(code, runtimeLanguage))
|
|
701
687
|
scheduleEditorTokenization(140, true)
|
|
702
688
|
applyEditorOptions()
|
|
703
689
|
scheduleEditorHeightSync()
|
|
@@ -724,6 +710,11 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
724
710
|
}
|
|
725
711
|
|
|
726
712
|
function cleanupEditor(disposeHelpers = true) {
|
|
713
|
+
if (disposeHelpers) {
|
|
714
|
+
lifecycleId += 1
|
|
715
|
+
createEditorPromise = null
|
|
716
|
+
ensureRuntimePromise = null
|
|
717
|
+
}
|
|
727
718
|
clearEditorHeightSyncBindings()
|
|
728
719
|
cancelEditorHeightSync()
|
|
729
720
|
try {
|
|
@@ -742,15 +733,15 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
742
733
|
lastLayoutHeight = null
|
|
743
734
|
if (disposeHelpers) {
|
|
744
735
|
helpers = null
|
|
745
|
-
|
|
746
|
-
|
|
736
|
+
runtimeOptions = null
|
|
737
|
+
ensureRuntimePromise = null
|
|
747
738
|
lastThemeRequest = ''
|
|
748
739
|
}
|
|
749
740
|
}
|
|
750
741
|
|
|
751
742
|
function applyEditorOptions() {
|
|
752
743
|
const target = diff ? helpers?.getDiffEditorView?.() : helpers?.getEditorView?.()
|
|
753
|
-
target?.updateOptions?.({ fontSize: codeFontSize
|
|
744
|
+
target?.updateOptions?.({ fontSize: codeFontSize })
|
|
754
745
|
syncEditorGeometryVars()
|
|
755
746
|
scheduleEditorHeightSync()
|
|
756
747
|
}
|
|
@@ -760,10 +751,10 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
760
751
|
function syncEditorGeometryVars() {
|
|
761
752
|
if (!editorHost)
|
|
762
753
|
return
|
|
763
|
-
const tabSize = readPositiveMetric(
|
|
754
|
+
const tabSize = readPositiveMetric(resolvedCodeBlockOptions?.tabSize) ?? 4
|
|
764
755
|
editorHost.style.setProperty('--diffs-tab-size', String(tabSize))
|
|
765
|
-
const rawPadding =
|
|
766
|
-
const hasConfiguredPadding =
|
|
756
|
+
const rawPadding = resolvedCodeBlockOptions?.padding
|
|
757
|
+
const hasConfiguredPadding = typeof rawPadding === 'number'
|
|
767
758
|
if (hasConfiguredPadding)
|
|
768
759
|
editorHost.style.setProperty('--diffs-gap-block', `${getCodePadding().top}px`)
|
|
769
760
|
else
|
|
@@ -771,11 +762,7 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
771
762
|
}
|
|
772
763
|
|
|
773
764
|
function getMaxHeightValue() {
|
|
774
|
-
|
|
775
|
-
if (raw === 'none' || raw == null)
|
|
776
|
-
return Number.POSITIVE_INFINITY
|
|
777
|
-
const value = typeof raw === 'number' ? raw : Number.parseFloat(String(raw))
|
|
778
|
-
return Number.isFinite(value) && value > 0 ? value : 500
|
|
765
|
+
return resolvedCodeBlockOptions?.maxHeight ?? 500
|
|
779
766
|
}
|
|
780
767
|
|
|
781
768
|
function scheduleEditorHeightSync() {
|
|
@@ -989,37 +976,19 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
989
976
|
if (hostRect.height <= 0)
|
|
990
977
|
return null
|
|
991
978
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
'
|
|
997
|
-
'.
|
|
998
|
-
'
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
for (const node of Array.from(container.querySelectorAll<HTMLElement>(selectors.join(',')))) {
|
|
1006
|
-
const style = window.getComputedStyle(node)
|
|
1007
|
-
if (style.display === 'none' || style.visibility === 'hidden')
|
|
1008
|
-
continue
|
|
1009
|
-
if (Number.parseFloat(style.opacity || '1') <= 0.01)
|
|
1010
|
-
continue
|
|
1011
|
-
const rect = node.getBoundingClientRect()
|
|
1012
|
-
if (rect.height <= 0 || rect.bottom <= hostRect.top)
|
|
1013
|
-
continue
|
|
1014
|
-
bottom = Math.max(bottom, rect.bottom - hostRect.top)
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
if (bottom > 0)
|
|
1018
|
-
return Math.ceil(bottom + 1)
|
|
1019
|
-
|
|
1020
|
-
const diffRoot = container.querySelector<HTMLElement>('.monaco-diff-editor')
|
|
1021
|
-
const diffHeight = diffRoot?.getBoundingClientRect?.().height ?? 0
|
|
1022
|
-
return diffHeight > 0 ? Math.ceil(diffHeight + 1) : null
|
|
979
|
+
// stream-diffs renders its surface inside these containers. Measure the
|
|
980
|
+
// rendered shell so a diff block fills its real content height even when
|
|
981
|
+
// the adapter's getContentHeight isn't available yet.
|
|
982
|
+
const surface = container.querySelector<HTMLElement>([
|
|
983
|
+
'diffs-container',
|
|
984
|
+
'.stream-diffs-shell',
|
|
985
|
+
'[data-stream-diffs-state]',
|
|
986
|
+
].join(','))
|
|
987
|
+
if (!surface)
|
|
988
|
+
return null
|
|
989
|
+
const rect = surface.getBoundingClientRect()
|
|
990
|
+
const height = rect.bottom - hostRect.top
|
|
991
|
+
return height > 0 ? Math.ceil(height + 1) : null
|
|
1023
992
|
}
|
|
1024
993
|
catch {
|
|
1025
994
|
return null
|
|
@@ -1073,7 +1042,7 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
1073
1042
|
{#if shouldRender}
|
|
1074
1043
|
<div
|
|
1075
1044
|
class:is-dark={resolvedIsDark}
|
|
1076
|
-
class:is-plain-text={
|
|
1045
|
+
class:is-plain-text={runtimeLanguage === 'plaintext'}
|
|
1077
1046
|
class:is-rendering={resolvedLoading}
|
|
1078
1047
|
class:is-diff={diff}
|
|
1079
1048
|
class="code-block-container"
|
|
@@ -1137,7 +1106,7 @@ ${configuredUnsafeCSS}`.trim()
|
|
|
1137
1106
|
class="code-pre-fallback"
|
|
1138
1107
|
enhanceable={false}
|
|
1139
1108
|
node={preFallbackNode}
|
|
1140
|
-
showLineNumbers={
|
|
1109
|
+
showLineNumbers={effectiveShowLineNumbers}
|
|
1141
1110
|
style={preFallbackStyle}
|
|
1142
1111
|
/>
|
|
1143
1112
|
</div>
|