feat: publish HoloLake model-native living system source
This commit is contained in:
parent
6ad10edde1
commit
c395dd3a99
2467 changed files with 615073 additions and 0 deletions
172
product-source/hololake-platform/src/utils/tldrawMarkdown.ts
Normal file
172
product-source/hololake-platform/src/utils/tldrawMarkdown.ts
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
import {
|
||||
type BlockLike,
|
||||
type DurableBlockCodec,
|
||||
type DurableFencePayloadInput,
|
||||
injectDurableMarkdownBlocks,
|
||||
preProcessDurableMarkdownBlocks,
|
||||
readCodeBlockLanguage,
|
||||
readInlineText,
|
||||
} from './durableMarkdownBlocks'
|
||||
|
||||
export const TLDRAW_BLOCK_TYPE = 'tldrawBlock'
|
||||
export const TLDRAW_DEFAULT_HEIGHT = '520'
|
||||
|
||||
const TOKEN_PREFIX = '@@TOLARIA_TLDRAW_BLOCK:'
|
||||
const TOKEN_SUFFIX = '@@'
|
||||
|
||||
interface TldrawPayload {
|
||||
boardId: string
|
||||
height: string
|
||||
snapshot: string
|
||||
width: string
|
||||
}
|
||||
|
||||
interface SnapshotSource {
|
||||
snapshot: string
|
||||
}
|
||||
|
||||
interface FenceAttribute {
|
||||
value: string
|
||||
}
|
||||
|
||||
interface FenceAttributeRequest {
|
||||
info: string
|
||||
name: 'height' | 'id' | 'width'
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
}
|
||||
|
||||
function decodeTldrawPayload(payload: unknown): TldrawPayload | null {
|
||||
if (!isRecord(payload)) return null
|
||||
if (typeof payload.boardId !== 'string') return null
|
||||
if (typeof payload.snapshot !== 'string') return null
|
||||
|
||||
return {
|
||||
boardId: payload.boardId,
|
||||
height: typeof payload.height === 'string' ? payload.height : TLDRAW_DEFAULT_HEIGHT,
|
||||
snapshot: payload.snapshot,
|
||||
width: typeof payload.width === 'string' ? payload.width : '',
|
||||
}
|
||||
}
|
||||
|
||||
function readFenceAttribute({ info, name }: FenceAttributeRequest): string {
|
||||
for (const match of info.matchAll(/\b([A-Za-z][\w-]*)=(?:"([^"]+)"|'([^']+)'|([^\s]+))/gu)) {
|
||||
if (match.at(1) === name) return match.at(2) ?? match.at(3) ?? match.at(4) ?? ''
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function readFenceMetadata(info: string): Pick<TldrawPayload, 'boardId' | 'height' | 'width'> {
|
||||
return {
|
||||
boardId: readFenceAttribute({ info, name: 'id' }),
|
||||
height: readFenceAttribute({ info, name: 'height' }) || TLDRAW_DEFAULT_HEIGHT,
|
||||
width: readFenceAttribute({ info, name: 'width' }),
|
||||
}
|
||||
}
|
||||
|
||||
function readTldrawFenceMetadata(info: string): Pick<TldrawPayload, 'boardId' | 'height' | 'width'> | null {
|
||||
const [language = '', ...infoParts] = info.trim().split(/\s+/u)
|
||||
if (language.toLowerCase() !== 'tldraw') return null
|
||||
return readFenceMetadata(infoParts.join(' '))
|
||||
}
|
||||
|
||||
function buildTldrawPayload({ lines, start, end, metadata }: DurableFencePayloadInput): TldrawPayload {
|
||||
const fenceMetadata = metadata as Pick<TldrawPayload, 'boardId' | 'height' | 'width'>
|
||||
return {
|
||||
...fenceMetadata,
|
||||
snapshot: lines.slice(start + 1, end).join('').trim(),
|
||||
}
|
||||
}
|
||||
|
||||
function buildTldrawBlock(block: BlockLike, payload: TldrawPayload): BlockLike {
|
||||
return {
|
||||
...block,
|
||||
type: TLDRAW_BLOCK_TYPE,
|
||||
props: {
|
||||
...(block.props ?? {}),
|
||||
boardId: payload.boardId,
|
||||
height: payload.height,
|
||||
snapshot: payload.snapshot,
|
||||
width: payload.width,
|
||||
},
|
||||
content: undefined,
|
||||
children: [],
|
||||
}
|
||||
}
|
||||
|
||||
function readTldrawCodeBlock(block: BlockLike): TldrawPayload | null {
|
||||
if (block.type !== 'codeBlock') return null
|
||||
if (readCodeBlockLanguage({ block }) !== 'tldraw') return null
|
||||
|
||||
const snapshot = readInlineText(block.content)
|
||||
if (snapshot === null) return null
|
||||
|
||||
return {
|
||||
boardId: '',
|
||||
height: TLDRAW_DEFAULT_HEIGHT,
|
||||
snapshot: snapshot.trim(),
|
||||
width: '',
|
||||
}
|
||||
}
|
||||
|
||||
function fenceLengthForSnapshot({ snapshot }: SnapshotSource): number {
|
||||
const longestRun = Math.max(0, ...Array.from(snapshot.matchAll(/`+/gu), match => match[0].length))
|
||||
return Math.max(3, longestRun + 1)
|
||||
}
|
||||
|
||||
function escapeFenceAttribute({ value }: FenceAttribute): string {
|
||||
return value.replace(/"/gu, '"')
|
||||
}
|
||||
|
||||
export function tldrawFenceSource({ boardId, height, snapshot, width }: TldrawPayload): string {
|
||||
const fence = '`'.repeat(fenceLengthForSnapshot({ snapshot }))
|
||||
const metadata = tldrawFenceMetadata({ boardId, height, width })
|
||||
const body = snapshot.endsWith('\n') ? snapshot : `${snapshot}\n`
|
||||
return `${fence}tldraw${metadata}\n${body}${fence}`
|
||||
}
|
||||
|
||||
function tldrawFenceMetadata({ boardId, height, width }: Omit<TldrawPayload, 'snapshot'>): string {
|
||||
const attributes: string[] = []
|
||||
if (boardId) attributes.push(`id="${escapeFenceAttribute({ value: boardId })}"`)
|
||||
if (height) attributes.push(`height="${escapeFenceAttribute({ value: height })}"`)
|
||||
if (width) attributes.push(`width="${escapeFenceAttribute({ value: width })}"`)
|
||||
return attributes.length > 0 ? ` ${attributes.join(' ')}` : ''
|
||||
}
|
||||
|
||||
export function isTldrawBlock(block: BlockLike): boolean {
|
||||
return block.type === TLDRAW_BLOCK_TYPE
|
||||
&& typeof block.props?.snapshot === 'string'
|
||||
&& typeof block.props?.boardId === 'string'
|
||||
}
|
||||
|
||||
export function tldrawMarkdown(block: BlockLike): string {
|
||||
const props = block.props ?? {}
|
||||
return tldrawFenceSource({
|
||||
boardId: props.boardId ?? '',
|
||||
height: props.height ?? TLDRAW_DEFAULT_HEIGHT,
|
||||
snapshot: props.snapshot ?? '{}',
|
||||
width: props.width ?? '',
|
||||
})
|
||||
}
|
||||
|
||||
export const tldrawMarkdownCodec: DurableBlockCodec = {
|
||||
tokenPrefix: TOKEN_PREFIX,
|
||||
tokenSuffix: TOKEN_SUFFIX,
|
||||
readFenceMetadata: readTldrawFenceMetadata,
|
||||
buildPayload: buildTldrawPayload,
|
||||
decodePayload: decodeTldrawPayload,
|
||||
buildBlock: (block, payload) => buildTldrawBlock(block, payload as TldrawPayload),
|
||||
readCodeBlock: readTldrawCodeBlock,
|
||||
isBlock: isTldrawBlock,
|
||||
serializeBlock: tldrawMarkdown,
|
||||
}
|
||||
|
||||
export function preProcessTldrawMarkdown({ markdown }: { markdown: string }): string {
|
||||
return preProcessDurableMarkdownBlocks({ markdown, codecs: [tldrawMarkdownCodec] })
|
||||
}
|
||||
|
||||
export function injectTldrawInBlocks(blocks: unknown[]): unknown[] {
|
||||
return injectDurableMarkdownBlocks({ blocks, codecs: [tldrawMarkdownCodec] })
|
||||
}
|
||||
Loading…
Reference in a new issue