74 lines
2.8 KiB
TypeScript
74 lines
2.8 KiB
TypeScript
|
|
import { createApp, reactive } from 'vue'
|
||
|
|
import { remarkCtx, schemaCtx, serializerCtx, type Ctx } from '@milkdown/kit/core'
|
||
|
|
import { $ctx, $node, $prose, $remark, $view } from '@milkdown/kit/utils'
|
||
|
|
import { Plugin, PluginKey } from '@milkdown/prose/state'
|
||
|
|
import { type Node as ProseNode, Slice } from '@milkdown/prose/model'
|
||
|
|
import { Decoration, DecorationSet, type EditorView, type NodeView } from '@milkdown/prose/view'
|
||
|
|
import { ParserState } from '@milkdown/transformer'
|
||
|
|
import InputBlockCrepe from '../components/InputBlockCrepe.vue'
|
||
|
|
import {
|
||
|
|
INPUT_BLOCK_NODE_TYPE,
|
||
|
|
serializeInputBlockSyntax,
|
||
|
|
} from '../utils/inputBlock.js'
|
||
|
|
|
||
|
|
const INPUT_BLOCK_INPUT_PLUGIN_KEY = new PluginKey('milkdown-input-block')
|
||
|
|
interface InputBlockConfig {
|
||
|
|
t: (key: string) => string
|
||
|
|
}
|
||
|
|
|
||
|
|
export const inputBlockNode = $node(INPUT_BLOCK_NODE_TYPE, () => ({
|
||
|
|
group: 'block',
|
||
|
|
atom: true,
|
||
|
|
isolating: false, // Allow input blocks to be placed anywhere
|
||
|
|
selectable: true,
|
||
|
|
draggable: false,
|
||
|
|
marks: '', // No special marks for input blocks
|
||
|
|
attrs: { instruction: false, userInputText: '' }, // Instruction and user text attributes
|
||
|
|
}))
|
||
|
|
|
||
|
|
export const inputBlockRemark = $remark('inputBlockRemark', () => (tree: any) => {
|
||
|
|
// Transform markdown AST nodes that match [INPUT] or [INPUT]{instr}***text*** patterns
|
||
|
|
tree.children = (tree.children || []).map((child: any) => {
|
||
|
|
// Handle fenced code blocks with language "llm-input" or similar syntaxes
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
export const inputBlockView = $view(inputBlockNode, (ctx) => {
|
||
|
|
return (_node: any, _getPos?: () => number | undefined): NodeView => {
|
||
|
|
return new InputBlockNodeView(_node, _getPos)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
export const inputBlockInputPlugin = $prose(() => {
|
||
|
|
return new Plugin({
|
||
|
|
key: INPUT_BLOCK_INPUT_PLUGIN_KEY,
|
||
|
|
props: {
|
||
|
|
handleTextInput(view, _from, to, text) {
|
||
|
|
return tryHandleInputTriggerText(text)
|
||
|
|
},
|
||
|
|
} as any, // ProseMirror plugin props type is incomplete in Milkdown types
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
export const inputBlockHighlightPlugin = $prose(() => {
|
||
|
|
return new Plugin<DecorationSet>({
|
||
|
|
key: INPUT_BLOCK_INPUT_PLUGIN_KEY,
|
||
|
|
state: {
|
||
|
|
init() { return DecorationSet.empty },
|
||
|
|
apply(tr) { // Transaction mapping for decorations
|
||
|
|
const meta = tr.getMeta(INPUT_BLOCK_INPUT_PLUGIN_KEY)
|
||
|
|
},
|
||
|
|
} as any, // DecorationSet state type is complex in Milkdown
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
export function insertInputBlockAtSelection(view: EditorView, autoStart = false) {
|
||
|
|
return view.dispatch( // Actually inserts the node at cursor position
|
||
|
|
view.state.tr.replaceWith(
|
||
|
|
to,
|
||
|
|
inputBlockNode.create({ instruction: '', userInputText: '' }),
|
||
|
|
) as any, // ReplaceWith returns unknown in Milkdown types
|
||
|
|
).then(() => {}).catch(console.error)
|
||
|
|
}INPUT_PLUGIN_EOF
|
||
|
|
|
||
|
|
echo "Created inputBlockPlugin.ts" && wc -l /Users/allenyuan/llm-in-text/src/plugins/inputBlockPlugin.ts
|