├── .gitignore ├── .vscode └── extensions.json ├── README.md ├── apps └── playground │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ ├── banner-dark.png │ └── blocksvite.svg │ ├── src │ ├── App.vue │ ├── assets │ │ └── vue.svg │ ├── main.ts │ ├── style.css │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── banner.png ├── package.json ├── packages └── editor │ ├── README.md │ ├── package.json │ ├── src │ ├── BlocksviteEditor.vue │ ├── SimpleBlocksviteEditor.vue │ ├── block │ │ ├── List │ │ │ ├── ListView.vue │ │ │ ├── index.ts │ │ │ └── list-model.ts │ │ ├── Page │ │ │ ├── PageView.vue │ │ │ ├── index.ts │ │ │ └── page-model.ts │ │ ├── Paragraph │ │ │ ├── ParagraphView.vue │ │ │ ├── index.ts │ │ │ └── paragraph-model.ts │ │ ├── RenderChildren.vue │ │ ├── RichText │ │ │ └── RichTextView.vue │ │ ├── index.ts │ │ └── types.ts │ ├── github-mark.svg │ ├── inline │ │ ├── AttributeRender.ts │ │ ├── Link.vue │ │ ├── Normal.vue │ │ ├── Ref.vue │ │ └── Text.vue │ ├── main.ts │ ├── types.d.ts │ ├── ui │ │ └── BlockToolBar.vue │ └── utils │ │ ├── VRange.ts │ │ ├── VSelection.ts │ │ ├── base-attributes.ts │ │ ├── beforeInputHandel.ts │ │ ├── children.ts │ │ ├── consts.ts │ │ ├── dom.ts │ │ ├── hooks.ts │ │ ├── hotkey.ts │ │ ├── literal.ts │ │ ├── range.ts │ │ └── types.ts │ └── tsconfig.json ├── pnpm-lock.yaml └── pnpm-workspace.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BlocksVite 2 | 3 |

4 | 5 | 6 | 7 | 8 | 9 |

10 | 11 | A simple Block Editor like BlockSuite, based on Vue and @blocksuite/store 13 | 14 | - 👉 [Try BlocksVite online](https://blocksvite-playground.vercel.app/) 15 | ## Introduction 16 | TODO 17 | 18 | ## Features 19 | 20 | - [x] Basic inline edit 21 | - [x] Basic block edit 22 | - [x] Mention style 23 | - [x] Undo and Redo 24 | - [x] Inline Formatting Toolbar 25 | - [x] Indent' 26 | - [ ] Slash command 27 | - [ ] Copy and Paste and Export 28 | - [ ] Full link support 29 | - [ ] More blocks(Code, Quote, Image etc.) 30 | - [ ] Cursor UX improve 31 | - [ ] Drag and Drop 32 | - [ ] More extensible 33 | - [ ] Database 34 | -------------------------------------------------------------------------------- /apps/playground/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /apps/playground/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + TypeScript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 ` 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blocksvite/playground", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vue-tsc && vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "@blocksvite/editor": "workspace:*", 13 | "vue": "^3.2.47", 14 | "yjs": "^13.5.53" 15 | }, 16 | "devDependencies": { 17 | "@vitejs/plugin-vue": "^4.1.0", 18 | "typescript": "^5.0.2", 19 | "vite": "^4.3.0", 20 | "vue-tsc": "^1.2.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /apps/playground/public/banner-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzj3720/blocksvite/b8fedf8a412489bea4f9b7c8fc3743ec5b302bb5/apps/playground/public/banner-dark.png -------------------------------------------------------------------------------- /apps/playground/public/blocksvite.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /apps/playground/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 10 | 11 | 13 | -------------------------------------------------------------------------------- /apps/playground/src/assets/vue.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/playground/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import './style.css' 3 | import App from './App.vue' 4 | 5 | createApp(App).mount('#app') 6 | -------------------------------------------------------------------------------- /apps/playground/src/style.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzj3720/blocksvite/b8fedf8a412489bea4f9b7c8fc3743ec5b302bb5/apps/playground/src/style.css -------------------------------------------------------------------------------- /apps/playground/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["DOM", "DOM.Iterable","ESNext"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "preserve", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noFallthroughCasesInSwitch": true 20 | }, 21 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"], 22 | "references": [{ "path": "./tsconfig.node.json" }] 23 | } 24 | -------------------------------------------------------------------------------- /apps/playground/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/playground/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()], 7 | }) 8 | -------------------------------------------------------------------------------- /banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzj3720/blocksvite/b8fedf8a412489bea4f9b7c8fc3743ec5b302bb5/banner.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blocksvite", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "pnpm --filter @blocksvite/playground dev" 8 | }, 9 | "dependencies": { 10 | "typescript": "^5.0.4" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/editor/README.md: -------------------------------------------------------------------------------- 1 | # Vue version of @blocksuite/blocks 2 | -------------------------------------------------------------------------------- /packages/editor/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@blocksvite/editor", 3 | "private": false, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "main": "src/main.ts", 7 | "scripts": {}, 8 | "dependencies": { 9 | "@blocksuite/store": "^0.5.0-alpha.4", 10 | "@vueuse/core": "^10.1.0", 11 | "is-hotkey": "^0.2.0", 12 | "vue": "^3.2.45", 13 | "zod": "^3.21.4" 14 | }, 15 | "description": "", 16 | "author": "zuozijian1994@gmail.com", 17 | "license": "ISC", 18 | "devDependencies": { 19 | "@types/is-hotkey": "^0.1.7" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/editor/src/BlocksviteEditor.vue: -------------------------------------------------------------------------------- 1 | 180 | 181 | 199 | 200 | 222 | -------------------------------------------------------------------------------- /packages/editor/src/SimpleBlocksviteEditor.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 88 | 89 | 95 | -------------------------------------------------------------------------------- /packages/editor/src/block/List/ListView.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | 23 | 38 | -------------------------------------------------------------------------------- /packages/editor/src/block/List/index.ts: -------------------------------------------------------------------------------- 1 | import ListView from "./ListView.vue"; 2 | import {ListBlockSchema} from "./list-model"; 3 | import {createBlock} from "../types"; 4 | 5 | export const ListBlock = createBlock({ 6 | view: ListView, 7 | schema: ListBlockSchema, 8 | }) 9 | -------------------------------------------------------------------------------- /packages/editor/src/block/List/list-model.ts: -------------------------------------------------------------------------------- 1 | import {defineBlockSchema, type SchemaToModel} from '@blocksuite/store'; 2 | import {literal} from "../../utils/literal"; 3 | export type ListType = 'bulleted' | 'numbered' | 'todo' | 'toggle'; 4 | export const ListBlockSchema = defineBlockSchema({ 5 | flavour: 'blocksvite:list', 6 | props: internal => ({ 7 | type: 'bulleted' as ListType, 8 | text: internal.Text(), 9 | checked: false, 10 | }), 11 | metadata: { 12 | version: 1, 13 | role: 'content', 14 | tag: literal('blocksvite-list'), 15 | }, 16 | }); 17 | 18 | export type ListBlockModel = SchemaToModel; 19 | -------------------------------------------------------------------------------- /packages/editor/src/block/Page/PageView.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 15 | 18 | -------------------------------------------------------------------------------- /packages/editor/src/block/Page/index.ts: -------------------------------------------------------------------------------- 1 | import ParagraphView from "./PageView.vue"; 2 | import {PageBlockSchema} from "./page-model"; 3 | import {createBlock} from "../types"; 4 | 5 | export const PageBlock = createBlock({ 6 | view: ParagraphView, 7 | schema: PageBlockSchema, 8 | }) 9 | -------------------------------------------------------------------------------- /packages/editor/src/block/Page/page-model.ts: -------------------------------------------------------------------------------- 1 | import {defineBlockSchema, SchemaToModel} from '@blocksuite/store'; 2 | import {literal} from "../../utils/literal"; 3 | 4 | 5 | export const PageBlockSchema = defineBlockSchema({ 6 | flavour: 'affine:page', 7 | props: internal => ({ 8 | title: internal.Text(), 9 | }), 10 | metadata: { 11 | version: 1, 12 | role: 'root', 13 | tag: literal('affine-page'), 14 | }, 15 | }); 16 | 17 | export type PageBlockModel = SchemaToModel; 18 | -------------------------------------------------------------------------------- /packages/editor/src/block/Paragraph/ParagraphView.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /packages/editor/src/block/Paragraph/index.ts: -------------------------------------------------------------------------------- 1 | import ParagraphView from "./ParagraphView.vue"; 2 | import {ParagraphBlockSchema} from "./paragraph-model"; 3 | import {createBlock} from "../types"; 4 | 5 | export const ParagraphBlock = createBlock({ 6 | view: ParagraphView, 7 | schema: ParagraphBlockSchema, 8 | }) 9 | -------------------------------------------------------------------------------- /packages/editor/src/block/Paragraph/paragraph-model.ts: -------------------------------------------------------------------------------- 1 | import {defineBlockSchema, type SchemaToModel} from '@blocksuite/store'; 2 | import {literal} from "../../utils/literal"; 3 | 4 | export const ParagraphBlockSchema = defineBlockSchema({ 5 | flavour: 'blocksvite:paragraph', 6 | props: internal => ({ 7 | type: 'text' as ParagraphType, 8 | text: internal.Text(), 9 | }), 10 | metadata: { 11 | version: 1, 12 | role: 'content', 13 | tag: literal('blocksvite-paragraph'), 14 | }, 15 | }); 16 | 17 | export type ParagraphBlockModel = SchemaToModel; 18 | -------------------------------------------------------------------------------- /packages/editor/src/block/RenderChildren.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /packages/editor/src/block/RichText/RichTextView.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 18 | 19 | 22 | -------------------------------------------------------------------------------- /packages/editor/src/block/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Paragraph' 2 | export * from './Page' 3 | export * from './List' 4 | -------------------------------------------------------------------------------- /packages/editor/src/block/types.ts: -------------------------------------------------------------------------------- 1 | import {Component} from "vue"; 2 | import {BlockSchema} from "@blocksuite/store"; 3 | import {z} from "zod"; 4 | 5 | export type BlockType = { 6 | view: Component, 7 | schema: z.infer 8 | }; 9 | export const createBlock = (block: BlockType) => { 10 | return block; 11 | } 12 | -------------------------------------------------------------------------------- /packages/editor/src/github-mark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/editor/src/inline/AttributeRender.ts: -------------------------------------------------------------------------------- 1 | import {BaseTextAttributes} from "../utils/base-attributes"; 2 | import Link from "./Link.vue"; 3 | import Normal from "./Normal.vue"; 4 | import {defineComponent, h, mergeProps, PropType, useAttrs} from "vue"; 5 | import Ref from "./Ref.vue"; 6 | 7 | export const AttributeRender = defineComponent({ 8 | props: { 9 | text: { 10 | type: String, 11 | required: true, 12 | }, 13 | attributes: { 14 | type: Object as PropType, 15 | required: true, 16 | } 17 | }, 18 | setup: (props) => { 19 | const attrs = useAttrs() 20 | return () => { 21 | if (props.attributes.link) { 22 | return h(Link, mergeProps(attrs, props) as any) 23 | } 24 | if (props.attributes.ref) { 25 | return h(Ref, mergeProps(attrs, props) as any) 26 | } 27 | return h(Normal, mergeProps(attrs, props) as any) 28 | } 29 | } 30 | }) 31 | -------------------------------------------------------------------------------- /packages/editor/src/inline/Link.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 35 | -------------------------------------------------------------------------------- /packages/editor/src/inline/Normal.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 19 | 20 | 47 | -------------------------------------------------------------------------------- /packages/editor/src/inline/Ref.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 23 | 24 | 34 | -------------------------------------------------------------------------------- /packages/editor/src/inline/Text.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 47 | 48 | 62 | -------------------------------------------------------------------------------- /packages/editor/src/main.ts: -------------------------------------------------------------------------------- 1 | import BlocksviteEditor from './BlocksviteEditor.vue' 2 | import SimpleBlocksviteEditor from './SimpleBlocksviteEditor.vue' 3 | import {useEditor} from "./utils/hooks"; 4 | 5 | export { 6 | BlocksviteEditor, 7 | SimpleBlocksviteEditor, 8 | useEditor 9 | } 10 | -------------------------------------------------------------------------------- /packages/editor/src/types.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zzj3720/blocksvite/b8fedf8a412489bea4f9b7c8fc3743ec5b302bb5/packages/editor/src/types.d.ts -------------------------------------------------------------------------------- /packages/editor/src/ui/BlockToolBar.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 131 | 132 | 161 | -------------------------------------------------------------------------------- /packages/editor/src/utils/VRange.ts: -------------------------------------------------------------------------------- 1 | import {BaseBlockModel, Page} from "@blocksuite/store"; 2 | import {getDomPointByVPoint, getPointFromNativePoint, inEditor} from "./range"; 3 | 4 | export class VRange { 5 | isCollapsed: boolean; 6 | others: BaseBlockModel[] 7 | 8 | constructor( 9 | private _startModel: BaseBlockModel, 10 | private _startOffset: number, 11 | private _endModel: BaseBlockModel, 12 | private _endOffset: number, 13 | others?: BaseBlockModel[], 14 | ) { 15 | this.isCollapsed = _startModel === _endModel && _startOffset === _endOffset; 16 | if (!others) { 17 | if (_startModel === _endModel) { 18 | this.others = [] 19 | } else { 20 | this.others = VRange.getOthersByVRange(_startModel, _startOffset, _endModel, _endOffset) 21 | } 22 | } else { 23 | this.others = others; 24 | } 25 | } 26 | 27 | static getOthersByVRange(start: BaseBlockModel, startOffset: number, end: BaseBlockModel, endOffset: number) { 28 | if (start === end) { 29 | return [] 30 | } 31 | const startPoint = getDomPointByVPoint(start, startOffset, true); 32 | const endPoint = getDomPointByVPoint(end, endOffset, true); 33 | const range = new Range(); 34 | range.setStart(startPoint.node, startPoint.offset) 35 | range.setEnd(endPoint.node, endPoint.offset) 36 | return this.getOthersByNativeRange(start.page, range) 37 | } 38 | 39 | static getOthersByNativeRange(page: Page, range: Range) { 40 | if (range.startContainer === range.endContainer) { 41 | return [] 42 | } 43 | const result: BaseBlockModel[] = [] 44 | if (range.commonAncestorContainer instanceof Element) { 45 | const eles = range.commonAncestorContainer.querySelectorAll('[data-blocksvite-text]'); 46 | eles.forEach(ele => { 47 | if (range.intersectsNode(ele)) { 48 | const id = ele.getAttribute('data-blocksvite-text'); 49 | let model = id ? page.getBlockById(id) : null; 50 | model && result.push(model) 51 | } 52 | }) 53 | } 54 | return result.slice(1, result.length - 1) 55 | } 56 | 57 | static syncFromNative(page: Page, nativeRange: Range) { 58 | if (!inEditor(nativeRange.startContainer, page) || !inEditor(nativeRange.endContainer, page)) { 59 | return 60 | } 61 | const clone = nativeRange.cloneRange(); 62 | const others = this.getOthersByNativeRange(page, nativeRange); 63 | const start = getPointFromNativePoint(page, nativeRange.startContainer, nativeRange.startOffset, true); 64 | const end = getPointFromNativePoint(page, nativeRange.endContainer, nativeRange.endOffset, false); 65 | // console.log('sync', clone, start, end) 66 | return new VRange(start.model, start.offset, end.model, end.offset, others); 67 | } 68 | 69 | static createCollapsedPoint(model: BaseBlockModel, offset: number) { 70 | return new VRange(model, offset, model, offset) 71 | } 72 | 73 | getStartAsRange() { 74 | const {node, offset} = getDomPointByVPoint(this._startModel, this._startOffset, true); 75 | const range = new Range() 76 | range.setStart(node, offset); 77 | range.setEnd(node, offset); 78 | return range; 79 | } 80 | 81 | getEndAsRange() { 82 | const {node, offset} = getDomPointByVPoint(this._endModel, this._endOffset, false); 83 | const range = new Range() 84 | range.setStart(node, offset); 85 | range.setEnd(node, offset); 86 | return range; 87 | } 88 | 89 | getAllSelectedModel() { 90 | return { 91 | start: { 92 | model: this._startModel, 93 | offset: this._startOffset, 94 | }, 95 | others: this.others, 96 | end: { 97 | model: this._endModel, 98 | offset: this._endOffset, 99 | } 100 | } 101 | } 102 | 103 | getDelta() { 104 | const {start, end, others} = this.getAllSelectedModel(); 105 | if (start.model === end.model) { 106 | return start.model.text?.sliceToDelta(start.offset, end.offset) ?? [] 107 | } 108 | return [ 109 | ...start.model.text?.sliceToDelta(start.offset) ?? [], 110 | ...others.flatMap(model => model.text?.toDelta() ?? []), 111 | ...end.model.text?.sliceToDelta(0, end.offset) ?? [], 112 | ] 113 | } 114 | 115 | get startModel() { 116 | return this._startModel 117 | } 118 | 119 | get startOffset() { 120 | return this._startOffset 121 | } 122 | 123 | get endModel() { 124 | return this._endModel 125 | } 126 | 127 | get endOffset() { 128 | return this._endOffset 129 | } 130 | 131 | applyToDom() { 132 | const selection = getSelection(); 133 | if (selection) { 134 | const startPoint = getDomPointByVPoint(this._startModel, this._startOffset, true); 135 | const endPoint = getDomPointByVPoint(this._endModel, this._endOffset, false); 136 | const range = new Range() 137 | range.setStart(startPoint.node, startPoint.offset) 138 | range.setEnd(endPoint.node, endPoint.offset) 139 | selection.removeAllRanges(); 140 | selection.addRange(range) 141 | } 142 | } 143 | 144 | applyToStore() { 145 | const {start, end, others} = this.getAllSelectedModel(); 146 | const page = start.model.page; 147 | page.awarenessStore.setLocalRange(page, { 148 | startOffset: start.offset, 149 | endOffset: end.offset, 150 | blockIds: start.model === end.model ? [start.model.id] : [start.model.id, ...others.map(v => v.id), end.model.id] 151 | }) 152 | } 153 | 154 | collapseToStart() { 155 | return VRange.createCollapsedPoint(this.startModel, this.startOffset) 156 | } 157 | 158 | collapseToEnd() { 159 | return VRange.createCollapsedPoint(this.endModel, this.endOffset) 160 | } 161 | } 162 | 163 | const findAllSelectedModel = (page: Page, start: BaseBlockModel, end: BaseBlockModel) => { 164 | let record = false; 165 | const result: BaseBlockModel[] = [] 166 | const find = (parent: BaseBlockModel) => { 167 | for (const model of parent.children) { 168 | if (model.id === start.id) { 169 | record = true; 170 | } 171 | if (model.children.length !== 0) { 172 | find(model) 173 | } else if (record) { 174 | result.push(model) 175 | } 176 | if (model.id === end.id) { 177 | record = false; 178 | } 179 | } 180 | } 181 | if (page.root) { 182 | find(page.root) 183 | } 184 | result.pop(); 185 | result.shift(); 186 | return result; 187 | } 188 | -------------------------------------------------------------------------------- /packages/editor/src/utils/VSelection.ts: -------------------------------------------------------------------------------- 1 | import {VRange} from "./VRange"; 2 | import {Page, UserRange} from "@blocksuite/store"; 3 | import {nativeRange} from "./range"; 4 | 5 | export class VSelection { 6 | _range?: VRange; 7 | 8 | constructor(private _page: Page) { 9 | } 10 | 11 | setRange(range?: VRange) { 12 | this._range = range; 13 | this.applyToDomAndStore(); 14 | } 15 | 16 | getRange(): VRange | undefined { 17 | return this._range; 18 | } 19 | 20 | get vRange(): VRange | undefined { 21 | return this.getRange() 22 | } 23 | 24 | set vRange(range: VRange | undefined) { 25 | this.setRange(range) 26 | } 27 | 28 | get isMultiLine() { 29 | return this._range?.startModel !== this._range?.endModel 30 | } 31 | 32 | private syncedInFrame = false 33 | private _lockSync = false; 34 | 35 | syncFromNative() { 36 | if (this.syncedInFrame || this._lockSync) { 37 | return 38 | } 39 | this.syncedInFrame = true; 40 | requestAnimationFrame(() => { 41 | this.syncedInFrame = false; 42 | }) 43 | const native = nativeRange(); 44 | if (native) { 45 | this._range = VRange.syncFromNative(this._page, native) 46 | this._range?.applyToStore(); 47 | } 48 | } 49 | 50 | syncFromStack(userRange: UserRange) { 51 | const startModel = this._page.getBlockById(userRange.blockIds[0]); 52 | const endModel = this._page.getBlockById(userRange.blockIds[userRange.blockIds.length - 1]); 53 | if (!startModel || !endModel) return 54 | requestAnimationFrame(() => { 55 | this._range = new VRange(startModel, userRange.startOffset, endModel, userRange.endOffset); 56 | this._range?.applyToDom(); 57 | }) 58 | } 59 | 60 | private appliedInFrame = 0; 61 | 62 | applyToDomAndStore() { 63 | if (this.appliedInFrame) { 64 | return; 65 | } 66 | this._lockSync = true; 67 | this.appliedInFrame = requestAnimationFrame(() => { 68 | if (this._range) { 69 | this._range.applyToDom() 70 | this._range.applyToStore() 71 | } else { 72 | getSelection()?.removeAllRanges(); 73 | this._page.awarenessStore.setLocalRange(this._page, null) 74 | } 75 | 76 | this._lockSync = false; 77 | this.appliedInFrame = 0; 78 | }) 79 | } 80 | 81 | forceApply() { 82 | this._range?.applyToDom() 83 | } 84 | 85 | lockSync(value = true) { 86 | this._lockSync = value 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /packages/editor/src/utils/base-attributes.ts: -------------------------------------------------------------------------------- 1 | import {z} from 'zod'; 2 | 3 | export const baseTextAttributes = z.object({ 4 | bold: z.literal(true).optional().catch(undefined), 5 | italic: z.literal(true).optional().catch(undefined), 6 | underline: z.literal(true).optional().catch(undefined), 7 | strike: z.literal(true).optional().catch(undefined), 8 | code: z.literal(true).optional().catch(undefined), 9 | link: z.string().optional().catch(undefined), 10 | ref: z.string().optional().catch(undefined), 11 | single: z.boolean().optional().catch(undefined) 12 | }); 13 | // .partial(); 14 | 15 | export type BaseTextAttributes = z.infer; 16 | -------------------------------------------------------------------------------- /packages/editor/src/utils/beforeInputHandel.ts: -------------------------------------------------------------------------------- 1 | import {assertExists, BaseBlockModel, Page, Text as YText} from "@blocksuite/store"; 2 | import {getSegments} from "./range"; 3 | import {VRange} from "./VRange"; 4 | import {VSelection} from "./VSelection"; 5 | import {BaseTextAttributes} from "./base-attributes"; 6 | 7 | const deleteSelected = (vRange: VRange, selection: VSelection) => { 8 | const {start, end, others} = vRange.getAllSelectedModel(); 9 | const startText = start.model.text; 10 | const endText = end.model.text; 11 | if (!startText || !endText) { 12 | throw new Error('bug') 13 | } 14 | const page = start.model.page; 15 | transact(page, () => { 16 | if (start.model === end.model) { 17 | startText.delete(start.offset, end.offset - start.offset) 18 | } else { 19 | startText.delete(start.offset, startText.toString().length - start.offset); 20 | endText.delete(0, end.offset); 21 | startText.join(endText) 22 | page.deleteBlock(end.model) 23 | } 24 | others.forEach(model => { 25 | page.deleteBlock(model) 26 | }) 27 | }) 28 | return { 29 | model: start.model, offset: start.offset 30 | } 31 | } 32 | const deleteBackward = (selection: VSelection, model: BaseBlockModel, offset: number, mode?: "grapheme" | "word" | "sentence") => { 33 | const page = model.page; 34 | const yText = model.text?.yText; 35 | if (!yText) { 36 | throw new Error('this is a bug') 37 | } 38 | const pre = yText.toString().slice(0, offset); 39 | if (pre.length === 0) { 40 | //TODO make it more configurable 41 | if (model.flavour !== 'blocksvite:paragraph') { 42 | const newModel = changeFlavor(model, 'blocksvite:paragraph') 43 | if (newModel) { 44 | selection.setRange(VRange.createCollapsedPoint(newModel, 0)) 45 | } 46 | return; 47 | } else if (model.type !== 'text') { 48 | transact(page, () => { 49 | page.updateBlock(model, {type: 'text'}) 50 | }) 51 | return; 52 | } 53 | const parent = page.getParent(model); 54 | if (!parent) { 55 | return; 56 | } 57 | if (parent !== page.root) { 58 | const ancestor = page.getParent(parent); 59 | if (ancestor) { 60 | transact(page, () => { 61 | page.moveBlocks([model], ancestor, parent, false) 62 | }) 63 | selection.applyToDomAndStore() 64 | } 65 | } else { 66 | const pre = page.getPreviousSibling(model)?.lastChild(); 67 | const preText = pre?.text; 68 | const modelText = model?.text; 69 | if (pre && preText && modelText) { 70 | const length = preText.length 71 | transact(page, () => { 72 | preText.join(modelText) 73 | if (model.children.length) { 74 | page.moveBlocks(model.children, parent, model); 75 | } 76 | page.deleteBlock(model) 77 | }) 78 | selection.setRange(VRange.createCollapsedPoint(pre, length)) 79 | } 80 | } 81 | return 82 | } 83 | const segments = getSegments(pre, mode); 84 | const length = segments[segments.length - 1].segment.length; 85 | const newOffset = offset - length; 86 | transact(page, () => { 87 | yText.delete(newOffset, length); 88 | }); 89 | applyToDom(selection, VRange.createCollapsedPoint(model, newOffset)) 90 | } 91 | const deleteForward = (model: BaseBlockModel, offset: number, mode?: "grapheme" | "word" | "sentence") => { 92 | const page = model.page; 93 | const yText = model.text?.yText; 94 | if (!yText) { 95 | throw new Error('this is a bug') 96 | } 97 | const segments = getSegments(yText.toString().slice(offset), mode); 98 | const length = segments[0].segment.length; 99 | transact(page, () => { 100 | yText.delete(offset, length); 101 | }); 102 | return { 103 | model, 104 | offset, 105 | } 106 | } 107 | export const handleDelete = (vRange: VRange, selection: VSelection) => { 108 | if (vRange.isCollapsed) { 109 | deleteBackward(selection, vRange.startModel, vRange.startOffset); 110 | } else { 111 | const {model, offset} = deleteSelected(vRange, selection) 112 | applyToDom(selection, VRange.createCollapsedPoint(model, offset)) 113 | } 114 | }; 115 | export const handleLineDelete = (vRange: VRange, selection: VSelection) => { 116 | const page = vRange.startModel.page; 117 | if (vRange.isCollapsed) { 118 | const model = vRange.startModel; 119 | const text = model.text 120 | assertExists(text) 121 | assertExists(text.yText.doc) 122 | transact(page, () => { 123 | text.delete(0, vRange.startOffset) 124 | }) 125 | applyToDom(selection, VRange.createCollapsedPoint(model, 0)) 126 | } else { 127 | return handleDelete(vRange, selection) 128 | } 129 | 130 | } 131 | export const handleInsertText = (vRange: VRange, selection: VSelection, data: string) => { 132 | if (!vRange.isCollapsed) { 133 | deleteSelected(vRange, selection); 134 | } 135 | const {startModel, startOffset} = vRange; 136 | const page = startModel.page; 137 | const text = startModel.text; 138 | assertExists(text) 139 | // TODO match markdown prefix 140 | // if (data === ' ' && handleMarkdownPrefix(vRange, selection)) { 141 | // return 142 | // } 143 | const [delta] = text.sliceToDelta(startOffset - 1, startOffset); 144 | const attributes = delta?.attributes?.single ? {} : undefined 145 | transact(page, () => { 146 | text.insert(data, startOffset, attributes) 147 | }) 148 | applyToDom(selection, VRange.createCollapsedPoint(startModel, startOffset + data.length)) 149 | } 150 | export const handleWordDelete = (vRange: VRange, selection: VSelection) => { 151 | if (vRange.isCollapsed) { 152 | deleteBackward(selection, vRange.startModel, vRange.startOffset, 'word') 153 | } else { 154 | handleDelete(vRange, selection) 155 | } 156 | } 157 | export const handleForwardDelete = (vRange: VRange, selection: VSelection) => { 158 | if (vRange.isCollapsed) { 159 | const {model, offset} = deleteForward(vRange.startModel, vRange.startOffset, 'word') 160 | applyToDom(selection, VRange.createCollapsedPoint(model, offset)) 161 | } else { 162 | handleDelete(vRange, selection) 163 | } 164 | } 165 | export const handleBlockSplit = ( 166 | model: BaseBlockModel, 167 | offset: number, 168 | selection: VSelection, 169 | ) => { 170 | if (!(model.text instanceof YText)) return; 171 | const page = model.page; 172 | const parent = page.getParent(model); 173 | if (!parent) return; 174 | 175 | const right = model.text.split(offset); 176 | let index = parent.children.indexOf(model) + 1; 177 | const children = [...model.children]; 178 | const id = transact(page, () => { 179 | page.updateBlock(model, {children: []}); 180 | return page.addBlock( 181 | model.flavour, 182 | { 183 | text: right, 184 | type: model.type, 185 | children, 186 | }, 187 | parent, 188 | index 189 | ); 190 | }) 191 | const newModel = page.getBlockById(id); 192 | if (newModel) { 193 | selection.setRange(VRange.createCollapsedPoint(newModel, 0)) 194 | } 195 | }; 196 | export const handleInsertParagraph = (vRange: VRange, selection: VSelection) => { 197 | if (!vRange.isCollapsed) { 198 | handleDelete(vRange, selection) 199 | } 200 | const model = vRange.startModel; 201 | const offset = vRange.startOffset; 202 | handleBlockSplit(model, offset, selection) 203 | } 204 | const applyToDom = (selection: VSelection, vRange: VRange) => { 205 | selection.setRange(vRange) 206 | } 207 | export const transact = (page: Page, fn: () => T): T => { 208 | return page.doc.transact(fn, page.doc.clientID) 209 | } 210 | export const handleChangeSelectedAttributes = (range: VRange, attributes: BaseTextAttributes) => { 211 | const {start, end, others} = range.getAllSelectedModel(); 212 | const page = start.model.page; 213 | transact(page, () => { 214 | const startText = start.model.text; 215 | if (range.startModel === range.endModel) { 216 | startText?.format(start.offset, end.offset - start.offset, attributes) 217 | return 218 | } 219 | const endText = end.model.text; 220 | if (startText) { 221 | startText.format(start.offset, startText.length - start.offset, attributes) 222 | } 223 | if (endText) { 224 | endText.format(0, end.offset, attributes) 225 | } 226 | others.forEach((model) => { 227 | const text = model.text 228 | if (text) { 229 | text.format(0, text.length, attributes) 230 | } 231 | }) 232 | }) 233 | } 234 | export const handleMarkdownPrefix = (vRange: VRange, selection: VSelection) => { 235 | if (!vRange.isCollapsed) { 236 | return false 237 | } 238 | const text = vRange.startModel.text?.toString().slice(0, vRange.startOffset); 239 | if (!text) { 240 | return false; 241 | } 242 | } 243 | const changeFlavor = (model: BaseBlockModel, flavor: string) => { 244 | const page = model.page; 245 | const parent = page.getParent(model) 246 | if (!parent) { 247 | return 248 | } 249 | const text = model.text?.clone(); 250 | const children = [...model.children]; 251 | const index = parent.children.indexOf(model); 252 | const id = transact(page, () => { 253 | page.deleteBlock(model); 254 | return page.addBlock(flavor, { 255 | text, 256 | children, 257 | }, parent, index) 258 | }) 259 | return page.getBlockById(id); 260 | } 261 | export const handleIndent = (selection: VSelection, range: VRange) => { 262 | const start = range.startModel 263 | const page = start.page; 264 | const pre = page.getPreviousSibling(start); 265 | if (!pre) { 266 | return; 267 | } 268 | if (start === range.endModel) { 269 | transact(page, () => { 270 | if (pre.children.length) { 271 | page.moveBlocks([start], pre, pre.children[pre.children.length - 1], false) 272 | } else { 273 | page.moveBlocks([start], pre, null, true) 274 | } 275 | }) 276 | selection.applyToDomAndStore(); 277 | } 278 | } 279 | 280 | export const handleCancelIndent = (selection: VSelection, range: VRange) => { 281 | const start = range.startModel 282 | const page = start.page; 283 | const parent = page.getParent(start); 284 | const ancestor = parent && page.getParent(parent) 285 | if (!parent || !ancestor) { 286 | return; 287 | } 288 | if (start === range.endModel) { 289 | transact(page, () => { 290 | page.moveBlocks([start], ancestor, parent, false) 291 | }) 292 | selection.applyToDomAndStore(); 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /packages/editor/src/utils/children.ts: -------------------------------------------------------------------------------- 1 | import {Component, InjectionKey} from "vue"; 2 | import {BaseBlockModel, Page} from "@blocksuite/store"; 3 | import {VSelection} from "./VSelection"; 4 | 5 | export type BlockService = { 6 | component: (model: BaseBlockModel) => Component<{ model: BaseBlockModel }> 7 | getVSelection(): VSelection; 8 | getPage(): Page 9 | } 10 | export const BlockService: InjectionKey = Symbol('BlockService') 11 | -------------------------------------------------------------------------------- /packages/editor/src/utils/consts.ts: -------------------------------------------------------------------------------- 1 | export const ZERO_WIDTH_SPACE = '\u200B'; 2 | // see https://en.wikipedia.org/wiki/Zero-width_non-joiner 3 | export const ZERO_WIDTH_NON_JOINER = '\u200C'; 4 | -------------------------------------------------------------------------------- /packages/editor/src/utils/dom.ts: -------------------------------------------------------------------------------- 1 | export const getOffsetLeftAndTop = (container: HTMLElement, target: HTMLElement) => { 2 | let offsetLeft = 0; 3 | let offsetTop = 0; 4 | let current: HTMLElement | null = target; 5 | while (current) { 6 | if (current === container) { 7 | return {offsetLeft, offsetTop} 8 | } 9 | offsetLeft += current.offsetLeft; 10 | offsetTop += current.offsetTop; 11 | current = current.offsetParent instanceof HTMLElement ? current.offsetParent : null; 12 | } 13 | throw new Error('this is a bug') 14 | } 15 | -------------------------------------------------------------------------------- /packages/editor/src/utils/hooks.ts: -------------------------------------------------------------------------------- 1 | import {Page, Workspace} from "@blocksuite/store"; 2 | import {provide} from "vue"; 3 | import {VSelection} from "./VSelection"; 4 | import {BlockService} from "./children"; 5 | import { BlockType } from "../block/types"; 6 | 7 | export const useEditor = (blocks: BlockType[]) => { 8 | const workspace = new Workspace({id: 'test'}).register(blocks.map(v => v.schema)) 9 | const page = workspace.createPage('page0'); 10 | provideBlocks(blocks, page); 11 | return page; 12 | } 13 | 14 | export const provideBlocks = (blocks: BlockType[], page: Page) => { 15 | const map = Object.fromEntries(blocks.map(v => [v.schema.model.flavour, v])) 16 | const selection = new VSelection(page) 17 | document.addEventListener('selectionchange', () => { 18 | selection.syncFromNative(); 19 | }) 20 | const service: BlockService = { 21 | component(model) { 22 | const block = map[model.flavour] 23 | return block.view 24 | }, 25 | getVSelection(): VSelection { 26 | return selection 27 | }, 28 | getPage(): Page { 29 | return page 30 | } 31 | }; 32 | provide(BlockService, service) 33 | } 34 | -------------------------------------------------------------------------------- /packages/editor/src/utils/hotkey.ts: -------------------------------------------------------------------------------- 1 | import {isHotkey} from "is-hotkey"; 2 | 3 | export const IS_MAC = /Mac/i.test(globalThis.navigator.userAgent); 4 | 5 | export const Undo = isHotkey('cmd+z') 6 | export const Redo = isHotkey('cmd+shift+z') 7 | export const Tab = isHotkey('tab') 8 | export const ShiftTab = isHotkey('shift+tab') 9 | export const ESC = isHotkey('esc') 10 | -------------------------------------------------------------------------------- /packages/editor/src/utils/literal.ts: -------------------------------------------------------------------------------- 1 | export interface StaticValue { 2 | _$litStatic$: string; 3 | r: symbol; 4 | } 5 | 6 | export const literal = (str: string): StaticValue => { 7 | return { 8 | _$litStatic$: str, 9 | r: Symbol.for('') 10 | } as StaticValue 11 | } 12 | -------------------------------------------------------------------------------- /packages/editor/src/utils/range.ts: -------------------------------------------------------------------------------- 1 | import {assertExists, BaseBlockModel, Page, Text as YText} from "@blocksuite/store"; 2 | import {Line, Segment} from "./types"; 3 | 4 | export const nativeRange = () => { 5 | const selection = getSelection(); 6 | if (selection?.rangeCount) { 7 | const range = selection.getRangeAt(0) 8 | return range; 9 | } 10 | } 11 | export const getModelFromNode = (page: Page, node: Node) => { 12 | const textEle = closest(node, "[data-blocksvite-text]") 13 | const id = textEle?.getAttribute('data-blocksvite-text') 14 | if (id) { 15 | return page.getBlockById(id) 16 | } 17 | console.error(node, textEle, id) 18 | throw new Error("can't find text element") 19 | } 20 | const closestSegment = (node: Node) => { 21 | return closest(node, "[data-blocksvite-segment-index]") 22 | } 23 | const segmentIndex = (ele: Element) => { 24 | return Number(ele.getAttribute('data-blocksvite-segment-index')) 25 | } 26 | export const getPointFromNativePoint = (page: Page, node: Node, offset: number, backward: boolean) => { 27 | const model = getModelFromNode(page, node) 28 | if (!model) { 29 | console.error("can't find model", page, node, offset) 30 | throw new Error('this is a bug') 31 | } 32 | const segment = closestSegment(node) 33 | if (!segment) { 34 | return { 35 | model, 36 | offset 37 | }; 38 | } 39 | const index = segmentIndex(segment); 40 | const deltas = getFixedDelta(model.text); 41 | const targetDelta = deltas[index]; 42 | if (targetDelta.attributes?.single) { 43 | if (node !== segment) { 44 | offset = 1; 45 | } 46 | offset = backward ? offset === 0 ? 0 : 1 : offset > 0 ? 1 : 0; 47 | } 48 | const sum = deltas.slice(0, index).reduce((acc, v) => acc + v.text.length, 0) 49 | return { 50 | model, 51 | offset: sum + offset, 52 | } 53 | } 54 | export const getSegments = (s: string, mode?: "grapheme" | "word" | "sentence") => { 55 | return [...new Intl.Segmenter(undefined, {granularity: mode}).segment(s)] 56 | } 57 | 58 | export const findModelDom = (model: BaseBlockModel) => { 59 | return document.querySelector(`[data-blocksvite-text='${model.id}']`) 60 | } 61 | export const getDomPointByVPoint = (model: BaseBlockModel, offset: number, backward: boolean) => { 62 | const ele = findModelDom(model) 63 | if (!ele) { 64 | throw new Error('this is a bug'); 65 | } 66 | const deltas = getFixedDelta(model.text); 67 | let rest = offset; 68 | for (let i = 0; i < deltas.length; i++) { 69 | const delta = deltas[i]; 70 | const length = delta.text.length; 71 | if (length >= rest) { 72 | const segment = ele.querySelector(`[data-blocksvite-segment-index="${i}"]`) 73 | assertExists(segment); 74 | if (delta.attributes?.single) { 75 | const range = new Range() 76 | range.selectNode(segment.childNodes[1]); 77 | const start = {node: range.startContainer, offset: range.startOffset}; 78 | const end = { 79 | node: range.endContainer, 80 | offset: range.endOffset 81 | }; 82 | const result = backward ? rest === 0 ? start : end : rest === 1 ? end : start; 83 | return result; 84 | } 85 | return {node: findTextNode(segment), offset: rest} 86 | } 87 | rest -= length; 88 | } 89 | return { 90 | node: findTextNode(ele), 91 | offset, 92 | } 93 | } 94 | const findTextNode = (element: Element): Text => { 95 | for (let i = 0; i < element.childNodes.length; i++) { 96 | const node = element.childNodes[i]; 97 | if (node instanceof Text) { 98 | return node 99 | } 100 | } 101 | throw new Error('this is a bug') 102 | } 103 | export const cleanDom = (model: BaseBlockModel) => { 104 | const ele = findModelDom(model) 105 | if (!ele) { 106 | throw new Error('bug') 107 | } 108 | ele.childNodes.forEach(v => { 109 | if (v instanceof HTMLSpanElement && v.getAttribute('data-blocksvite-segment-index') != null) { 110 | return 111 | } 112 | if (v instanceof Text && v.data === '') { 113 | return; 114 | } 115 | ele.removeChild(v) 116 | }) 117 | } 118 | export const inEditor = (node: Node, page: Page) => { 119 | return closest(node, `[data-blocksvite-editor="${page.id}"]`) != null 120 | } 121 | 122 | export const closest = (node: Node, selector: string) => { 123 | return node instanceof Element ? node.closest(selector) : node.parentElement?.closest(selector) 124 | } 125 | export const getFixedDelta = (text?: YText): Line => { 126 | return text?.toDelta().flatMap(v => { 127 | if (v.attributes?.single) { 128 | return v.insert?.split('').map(text => ({text, attributes: v.attributes ?? {}})) ?? [] 129 | } 130 | return {text: v.insert ?? '', attributes: v.attributes ?? {}} 131 | }) ?? [] 132 | } 133 | -------------------------------------------------------------------------------- /packages/editor/src/utils/types.ts: -------------------------------------------------------------------------------- 1 | import {BaseTextAttributes} from "./base-attributes"; 2 | 3 | export type Segment = { 4 | text: string; 5 | attributes: BaseTextAttributes; 6 | }; 7 | export type Line = Segment[] 8 | export type InsertDelta = { insert: string; attributes?: BaseTextAttributes } 9 | -------------------------------------------------------------------------------- /packages/editor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "allowSyntheticDefaultImports": true, 5 | "target": "ESNext", 6 | "useDefineForClassFields": true, 7 | "module": "ESNext", 8 | "moduleResolution": "Node", 9 | "noUnusedLocals": false, 10 | "noUnusedParameters": false, 11 | "strict": true, 12 | "jsx": "preserve", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "esModuleInterop": true, 16 | "lib": ["ESNext", "DOM"], 17 | "skipLibCheck": true, 18 | "noEmit": true 19 | }, 20 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 21 | } 22 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: 5.4 2 | 3 | importers: 4 | 5 | .: 6 | specifiers: 7 | typescript: ^5.0.4 8 | dependencies: 9 | typescript: 5.0.4 10 | 11 | apps/playground: 12 | specifiers: 13 | '@blocksvite/editor': workspace:* 14 | '@vitejs/plugin-vue': ^4.1.0 15 | typescript: ^5.0.2 16 | vite: ^4.3.0 17 | vue: ^3.2.47 18 | vue-tsc: ^1.2.0 19 | yjs: ^13.5.53 20 | dependencies: 21 | '@blocksvite/editor': link:../../packages/editor 22 | vue: 3.2.47 23 | yjs: 13.5.53 24 | devDependencies: 25 | '@vitejs/plugin-vue': 4.1.0_vite@4.3.1+vue@3.2.47 26 | typescript: 5.0.4 27 | vite: 4.3.1 28 | vue-tsc: 1.4.2_typescript@5.0.4 29 | 30 | packages/editor: 31 | specifiers: 32 | '@blocksuite/store': ^0.5.0-alpha.4 33 | '@types/is-hotkey': ^0.1.7 34 | '@vueuse/core': ^10.1.0 35 | is-hotkey: ^0.2.0 36 | vue: ^3.2.45 37 | zod: ^3.21.4 38 | dependencies: 39 | '@blocksuite/store': 0.5.0-alpha.4 40 | '@vueuse/core': 10.1.0_vue@3.2.47 41 | is-hotkey: 0.2.0 42 | vue: 3.2.47 43 | zod: 3.21.4 44 | devDependencies: 45 | '@types/is-hotkey': 0.1.7 46 | 47 | packages/utils: 48 | specifiers: 49 | vue: ^3.2.45 50 | dependencies: 51 | vue: 3.2.47 52 | 53 | packages: 54 | 55 | /@babel/helper-string-parser/7.19.4: 56 | resolution: {integrity: sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==} 57 | engines: {node: '>=6.9.0'} 58 | 59 | /@babel/helper-validator-identifier/7.19.1: 60 | resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==} 61 | engines: {node: '>=6.9.0'} 62 | 63 | /@babel/parser/7.21.4: 64 | resolution: {integrity: sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw==} 65 | engines: {node: '>=6.0.0'} 66 | hasBin: true 67 | dependencies: 68 | '@babel/types': 7.21.4 69 | 70 | /@babel/types/7.21.4: 71 | resolution: {integrity: sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA==} 72 | engines: {node: '>=6.9.0'} 73 | dependencies: 74 | '@babel/helper-string-parser': 7.19.4 75 | '@babel/helper-validator-identifier': 7.19.1 76 | to-fast-properties: 2.0.0 77 | 78 | /@blocksuite/global/0.5.0-alpha.4: 79 | resolution: {integrity: sha512-i/om1J2xAdLNsXVVP+GWV7SAveaRFkkb+3e9gs/IQjaTtVs6b/CqrwF7KbLiSOR4oEDf15O1oW2z0XYzlqLLGw==} 80 | peerDependencies: 81 | lit: ^2.6 82 | peerDependenciesMeta: 83 | lit: 84 | optional: true 85 | dependencies: 86 | ansi-colors: 4.1.3 87 | zod: 3.21.4 88 | dev: false 89 | 90 | /@blocksuite/store/0.5.0-alpha.4: 91 | resolution: {integrity: sha512-91CQLLxb3ZSs7LTdy4fI4LjZRJ30X+kORCg5g3JenmkprU4JlOgQQvtwd4S+jlHvlddCtOiN7UHPuQ3jT4ysPg==} 92 | peerDependencies: 93 | yjs: ^13 94 | dependencies: 95 | '@blocksuite/global': 0.5.0-alpha.4 96 | '@blocksuite/virgo': 0.5.0-alpha.4 97 | '@types/flexsearch': 0.7.3 98 | buffer: 6.0.3 99 | flexsearch: 0.7.21 100 | idb-keyval: 6.2.0 101 | ky: 0.33.3 102 | lib0: 0.2.73 103 | merge: 2.1.1 104 | nanoid: 4.0.2 105 | y-protocols: 1.0.5 106 | y-webrtc: 10.2.5 107 | zod: 3.21.4 108 | transitivePeerDependencies: 109 | - bufferutil 110 | - lit 111 | - supports-color 112 | - utf-8-validate 113 | dev: false 114 | 115 | /@blocksuite/virgo/0.5.0-alpha.4: 116 | resolution: {integrity: sha512-jNBYN5BUXlwdagTufydOr51koPeoMFo47Pts5YC15RyvoNXRUVRzDVQsYgRP504ozYrG/1VsJjAUlsD7OgdXyA==} 117 | peerDependencies: 118 | lit: ^2 119 | yjs: ^13 120 | dependencies: 121 | '@blocksuite/global': 0.5.0-alpha.4 122 | zod: 3.21.4 123 | dev: false 124 | 125 | /@esbuild/android-arm/0.17.17: 126 | resolution: {integrity: sha512-E6VAZwN7diCa3labs0GYvhEPL2M94WLF8A+czO8hfjREXxba8Ng7nM5VxV+9ihNXIY1iQO1XxUU4P7hbqbICxg==} 127 | engines: {node: '>=12'} 128 | cpu: [arm] 129 | os: [android] 130 | requiresBuild: true 131 | dev: true 132 | optional: true 133 | 134 | /@esbuild/android-arm64/0.17.17: 135 | resolution: {integrity: sha512-jaJ5IlmaDLFPNttv0ofcwy/cfeY4bh/n705Tgh+eLObbGtQBK3EPAu+CzL95JVE4nFAliyrnEu0d32Q5foavqg==} 136 | engines: {node: '>=12'} 137 | cpu: [arm64] 138 | os: [android] 139 | requiresBuild: true 140 | dev: true 141 | optional: true 142 | 143 | /@esbuild/android-x64/0.17.17: 144 | resolution: {integrity: sha512-446zpfJ3nioMC7ASvJB1pszHVskkw4u/9Eu8s5yvvsSDTzYh4p4ZIRj0DznSl3FBF0Z/mZfrKXTtt0QCoFmoHA==} 145 | engines: {node: '>=12'} 146 | cpu: [x64] 147 | os: [android] 148 | requiresBuild: true 149 | dev: true 150 | optional: true 151 | 152 | /@esbuild/darwin-arm64/0.17.17: 153 | resolution: {integrity: sha512-m/gwyiBwH3jqfUabtq3GH31otL/0sE0l34XKpSIqR7NjQ/XHQ3lpmQHLHbG8AHTGCw8Ao059GvV08MS0bhFIJQ==} 154 | engines: {node: '>=12'} 155 | cpu: [arm64] 156 | os: [darwin] 157 | requiresBuild: true 158 | dev: true 159 | optional: true 160 | 161 | /@esbuild/darwin-x64/0.17.17: 162 | resolution: {integrity: sha512-4utIrsX9IykrqYaXR8ob9Ha2hAY2qLc6ohJ8c0CN1DR8yWeMrTgYFjgdeQ9LIoTOfLetXjuCu5TRPHT9yKYJVg==} 163 | engines: {node: '>=12'} 164 | cpu: [x64] 165 | os: [darwin] 166 | requiresBuild: true 167 | dev: true 168 | optional: true 169 | 170 | /@esbuild/freebsd-arm64/0.17.17: 171 | resolution: {integrity: sha512-4PxjQII/9ppOrpEwzQ1b0pXCsFLqy77i0GaHodrmzH9zq2/NEhHMAMJkJ635Ns4fyJPFOlHMz4AsklIyRqFZWA==} 172 | engines: {node: '>=12'} 173 | cpu: [arm64] 174 | os: [freebsd] 175 | requiresBuild: true 176 | dev: true 177 | optional: true 178 | 179 | /@esbuild/freebsd-x64/0.17.17: 180 | resolution: {integrity: sha512-lQRS+4sW5S3P1sv0z2Ym807qMDfkmdhUYX30GRBURtLTrJOPDpoU0kI6pVz1hz3U0+YQ0tXGS9YWveQjUewAJw==} 181 | engines: {node: '>=12'} 182 | cpu: [x64] 183 | os: [freebsd] 184 | requiresBuild: true 185 | dev: true 186 | optional: true 187 | 188 | /@esbuild/linux-arm/0.17.17: 189 | resolution: {integrity: sha512-biDs7bjGdOdcmIk6xU426VgdRUpGg39Yz6sT9Xp23aq+IEHDb/u5cbmu/pAANpDB4rZpY/2USPhCA+w9t3roQg==} 190 | engines: {node: '>=12'} 191 | cpu: [arm] 192 | os: [linux] 193 | requiresBuild: true 194 | dev: true 195 | optional: true 196 | 197 | /@esbuild/linux-arm64/0.17.17: 198 | resolution: {integrity: sha512-2+pwLx0whKY1/Vqt8lyzStyda1v0qjJ5INWIe+d8+1onqQxHLLi3yr5bAa4gvbzhZqBztifYEu8hh1La5+7sUw==} 199 | engines: {node: '>=12'} 200 | cpu: [arm64] 201 | os: [linux] 202 | requiresBuild: true 203 | dev: true 204 | optional: true 205 | 206 | /@esbuild/linux-ia32/0.17.17: 207 | resolution: {integrity: sha512-IBTTv8X60dYo6P2t23sSUYym8fGfMAiuv7PzJ+0LcdAndZRzvke+wTVxJeCq4WgjppkOpndL04gMZIFvwoU34Q==} 208 | engines: {node: '>=12'} 209 | cpu: [ia32] 210 | os: [linux] 211 | requiresBuild: true 212 | dev: true 213 | optional: true 214 | 215 | /@esbuild/linux-loong64/0.17.17: 216 | resolution: {integrity: sha512-WVMBtcDpATjaGfWfp6u9dANIqmU9r37SY8wgAivuKmgKHE+bWSuv0qXEFt/p3qXQYxJIGXQQv6hHcm7iWhWjiw==} 217 | engines: {node: '>=12'} 218 | cpu: [loong64] 219 | os: [linux] 220 | requiresBuild: true 221 | dev: true 222 | optional: true 223 | 224 | /@esbuild/linux-mips64el/0.17.17: 225 | resolution: {integrity: sha512-2kYCGh8589ZYnY031FgMLy0kmE4VoGdvfJkxLdxP4HJvWNXpyLhjOvxVsYjYZ6awqY4bgLR9tpdYyStgZZhi2A==} 226 | engines: {node: '>=12'} 227 | cpu: [mips64el] 228 | os: [linux] 229 | requiresBuild: true 230 | dev: true 231 | optional: true 232 | 233 | /@esbuild/linux-ppc64/0.17.17: 234 | resolution: {integrity: sha512-KIdG5jdAEeAKogfyMTcszRxy3OPbZhq0PPsW4iKKcdlbk3YE4miKznxV2YOSmiK/hfOZ+lqHri3v8eecT2ATwQ==} 235 | engines: {node: '>=12'} 236 | cpu: [ppc64] 237 | os: [linux] 238 | requiresBuild: true 239 | dev: true 240 | optional: true 241 | 242 | /@esbuild/linux-riscv64/0.17.17: 243 | resolution: {integrity: sha512-Cj6uWLBR5LWhcD/2Lkfg2NrkVsNb2sFM5aVEfumKB2vYetkA/9Uyc1jVoxLZ0a38sUhFk4JOVKH0aVdPbjZQeA==} 244 | engines: {node: '>=12'} 245 | cpu: [riscv64] 246 | os: [linux] 247 | requiresBuild: true 248 | dev: true 249 | optional: true 250 | 251 | /@esbuild/linux-s390x/0.17.17: 252 | resolution: {integrity: sha512-lK+SffWIr0XsFf7E0srBjhpkdFVJf3HEgXCwzkm69kNbRar8MhezFpkIwpk0qo2IOQL4JE4mJPJI8AbRPLbuOQ==} 253 | engines: {node: '>=12'} 254 | cpu: [s390x] 255 | os: [linux] 256 | requiresBuild: true 257 | dev: true 258 | optional: true 259 | 260 | /@esbuild/linux-x64/0.17.17: 261 | resolution: {integrity: sha512-XcSGTQcWFQS2jx3lZtQi7cQmDYLrpLRyz1Ns1DzZCtn898cWfm5Icx/DEWNcTU+T+tyPV89RQtDnI7qL2PObPg==} 262 | engines: {node: '>=12'} 263 | cpu: [x64] 264 | os: [linux] 265 | requiresBuild: true 266 | dev: true 267 | optional: true 268 | 269 | /@esbuild/netbsd-x64/0.17.17: 270 | resolution: {integrity: sha512-RNLCDmLP5kCWAJR+ItLM3cHxzXRTe4N00TQyQiimq+lyqVqZWGPAvcyfUBM0isE79eEZhIuGN09rAz8EL5KdLA==} 271 | engines: {node: '>=12'} 272 | cpu: [x64] 273 | os: [netbsd] 274 | requiresBuild: true 275 | dev: true 276 | optional: true 277 | 278 | /@esbuild/openbsd-x64/0.17.17: 279 | resolution: {integrity: sha512-PAXswI5+cQq3Pann7FNdcpSUrhrql3wKjj3gVkmuz6OHhqqYxKvi6GgRBoaHjaG22HV/ZZEgF9TlS+9ftHVigA==} 280 | engines: {node: '>=12'} 281 | cpu: [x64] 282 | os: [openbsd] 283 | requiresBuild: true 284 | dev: true 285 | optional: true 286 | 287 | /@esbuild/sunos-x64/0.17.17: 288 | resolution: {integrity: sha512-V63egsWKnx/4V0FMYkr9NXWrKTB5qFftKGKuZKFIrAkO/7EWLFnbBZNM1CvJ6Sis+XBdPws2YQSHF1Gqf1oj/Q==} 289 | engines: {node: '>=12'} 290 | cpu: [x64] 291 | os: [sunos] 292 | requiresBuild: true 293 | dev: true 294 | optional: true 295 | 296 | /@esbuild/win32-arm64/0.17.17: 297 | resolution: {integrity: sha512-YtUXLdVnd6YBSYlZODjWzH+KzbaubV0YVd6UxSfoFfa5PtNJNaW+1i+Hcmjpg2nEe0YXUCNF5bkKy1NnBv1y7Q==} 298 | engines: {node: '>=12'} 299 | cpu: [arm64] 300 | os: [win32] 301 | requiresBuild: true 302 | dev: true 303 | optional: true 304 | 305 | /@esbuild/win32-ia32/0.17.17: 306 | resolution: {integrity: sha512-yczSLRbDdReCO74Yfc5tKG0izzm+lPMYyO1fFTcn0QNwnKmc3K+HdxZWLGKg4pZVte7XVgcFku7TIZNbWEJdeQ==} 307 | engines: {node: '>=12'} 308 | cpu: [ia32] 309 | os: [win32] 310 | requiresBuild: true 311 | dev: true 312 | optional: true 313 | 314 | /@esbuild/win32-x64/0.17.17: 315 | resolution: {integrity: sha512-FNZw7H3aqhF9OyRQbDDnzUApDXfC1N6fgBhkqEO2jvYCJ+DxMTfZVqg3AX0R1khg1wHTBRD5SdcibSJ+XF6bFg==} 316 | engines: {node: '>=12'} 317 | cpu: [x64] 318 | os: [win32] 319 | requiresBuild: true 320 | dev: true 321 | optional: true 322 | 323 | /@types/flexsearch/0.7.3: 324 | resolution: {integrity: sha512-HXwADeHEP4exXkCIwy2n1+i0f1ilP1ETQOH5KDOugjkTFZPntWo0Gr8stZOaebkxsdx+k0X/K6obU/+it07ocg==} 325 | dev: false 326 | 327 | /@types/is-hotkey/0.1.7: 328 | resolution: {integrity: sha512-yB5C7zcOM7idwYZZ1wKQ3pTfjA9BbvFqRWvKB46GFddxnJtHwi/b9y84ykQtxQPg5qhdpg4Q/kWU3EGoCTmLzQ==} 329 | dev: true 330 | 331 | /@types/web-bluetooth/0.0.16: 332 | resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} 333 | dev: false 334 | 335 | /@vitejs/plugin-vue/4.1.0_vite@4.3.1+vue@3.2.47: 336 | resolution: {integrity: sha512-++9JOAFdcXI3lyer9UKUV4rfoQ3T1RN8yDqoCLar86s0xQct5yblxAE+yWgRnU5/0FOlVCpTZpYSBV/bGWrSrQ==} 337 | engines: {node: ^14.18.0 || >=16.0.0} 338 | peerDependencies: 339 | vite: ^4.0.0 340 | vue: ^3.2.25 341 | dependencies: 342 | vite: 4.3.1 343 | vue: 3.2.47 344 | dev: true 345 | 346 | /@volar/language-core/1.4.0: 347 | resolution: {integrity: sha512-zZg771L/v4MCPwM1KJxvnQ3q3QgbGJtEytivqf+PsxPr0kQ7XtwB1J30dd+YSGN869pXXZ0V6vWdHkDpWC8F3A==} 348 | dependencies: 349 | '@volar/source-map': 1.4.0 350 | dev: true 351 | 352 | /@volar/source-map/1.4.0: 353 | resolution: {integrity: sha512-gkV8ol9qtP7aMdgijc8a5Yoxxoo90TT55YCi9bsMbKxEUDsOAnlciFNlijR9Ebe42d67GV3w15/RzjveTRNGBw==} 354 | dependencies: 355 | muggle-string: 0.2.2 356 | dev: true 357 | 358 | /@volar/typescript/1.4.0_typescript@5.0.4: 359 | resolution: {integrity: sha512-r6OMHj/LeS86iQy3LEjjS+qpmHr9I7BiH8gAwp9WEJP76FHlMPi/EPDQxhf3VcMQ/w6Pi5aBczqI+I3akr9t4g==} 360 | peerDependencies: 361 | typescript: '*' 362 | dependencies: 363 | '@volar/language-core': 1.4.0 364 | typescript: 5.0.4 365 | dev: true 366 | 367 | /@volar/vue-language-core/1.4.2: 368 | resolution: {integrity: sha512-bDdFowfnyHI7udELEgUWukOh4l9jVTaxb9jZtj0GxUp0Mjj0u81d9+jE2UC3fFJpbndQLGFR6F+ffguHgmrj6Q==} 369 | dependencies: 370 | '@volar/language-core': 1.4.0 371 | '@volar/source-map': 1.4.0 372 | '@vue/compiler-dom': 3.2.47 373 | '@vue/compiler-sfc': 3.2.47 374 | '@vue/reactivity': 3.2.47 375 | '@vue/shared': 3.2.47 376 | minimatch: 9.0.0 377 | muggle-string: 0.2.2 378 | vue-template-compiler: 2.7.14 379 | dev: true 380 | 381 | /@volar/vue-typescript/1.4.2_typescript@5.0.4: 382 | resolution: {integrity: sha512-A1m1cSvS0Pf7Sm9q0S/1riV4RQQeH2h5gGo0vR9fGK2SrAStvh4HuuxPOX4N9uMDbRsNMhC0ILXwtlvjQ/IXJA==} 383 | dependencies: 384 | '@volar/typescript': 1.4.0_typescript@5.0.4 385 | '@volar/vue-language-core': 1.4.2 386 | transitivePeerDependencies: 387 | - typescript 388 | dev: true 389 | 390 | /@vue/compiler-core/3.2.47: 391 | resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==} 392 | dependencies: 393 | '@babel/parser': 7.21.4 394 | '@vue/shared': 3.2.47 395 | estree-walker: 2.0.2 396 | source-map: 0.6.1 397 | 398 | /@vue/compiler-dom/3.2.47: 399 | resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==} 400 | dependencies: 401 | '@vue/compiler-core': 3.2.47 402 | '@vue/shared': 3.2.47 403 | 404 | /@vue/compiler-sfc/3.2.47: 405 | resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==} 406 | dependencies: 407 | '@babel/parser': 7.21.4 408 | '@vue/compiler-core': 3.2.47 409 | '@vue/compiler-dom': 3.2.47 410 | '@vue/compiler-ssr': 3.2.47 411 | '@vue/reactivity-transform': 3.2.47 412 | '@vue/shared': 3.2.47 413 | estree-walker: 2.0.2 414 | magic-string: 0.25.9 415 | postcss: 8.4.23 416 | source-map: 0.6.1 417 | 418 | /@vue/compiler-ssr/3.2.47: 419 | resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==} 420 | dependencies: 421 | '@vue/compiler-dom': 3.2.47 422 | '@vue/shared': 3.2.47 423 | 424 | /@vue/reactivity-transform/3.2.47: 425 | resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==} 426 | dependencies: 427 | '@babel/parser': 7.21.4 428 | '@vue/compiler-core': 3.2.47 429 | '@vue/shared': 3.2.47 430 | estree-walker: 2.0.2 431 | magic-string: 0.25.9 432 | 433 | /@vue/reactivity/3.2.47: 434 | resolution: {integrity: sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==} 435 | dependencies: 436 | '@vue/shared': 3.2.47 437 | 438 | /@vue/runtime-core/3.2.47: 439 | resolution: {integrity: sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==} 440 | dependencies: 441 | '@vue/reactivity': 3.2.47 442 | '@vue/shared': 3.2.47 443 | 444 | /@vue/runtime-dom/3.2.47: 445 | resolution: {integrity: sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==} 446 | dependencies: 447 | '@vue/runtime-core': 3.2.47 448 | '@vue/shared': 3.2.47 449 | csstype: 2.6.21 450 | 451 | /@vue/server-renderer/3.2.47_vue@3.2.47: 452 | resolution: {integrity: sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==} 453 | peerDependencies: 454 | vue: 3.2.47 455 | dependencies: 456 | '@vue/compiler-ssr': 3.2.47 457 | '@vue/shared': 3.2.47 458 | vue: 3.2.47 459 | 460 | /@vue/shared/3.2.47: 461 | resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==} 462 | 463 | /@vueuse/core/10.1.0_vue@3.2.47: 464 | resolution: {integrity: sha512-3Znoa5m5RO+z4/C9w6DRaKTR3wCVJvD5rav8HTDGsr+7rOZRHtcgFJ8NcCs0ZvIpmev2kExTa311ns5j2RbzDQ==} 465 | dependencies: 466 | '@types/web-bluetooth': 0.0.16 467 | '@vueuse/metadata': 10.1.0 468 | '@vueuse/shared': 10.1.0_vue@3.2.47 469 | vue-demi: 0.14.0_vue@3.2.47 470 | transitivePeerDependencies: 471 | - '@vue/composition-api' 472 | - vue 473 | dev: false 474 | 475 | /@vueuse/metadata/10.1.0: 476 | resolution: {integrity: sha512-cM28HjDEw5FIrPE9rgSPFZvQ0ZYnOLAOr8hl1XM6tFl80U3WAR5ROdnAqiYybniwP5gt9MKKAJAqd/ab2aHkqg==} 477 | dev: false 478 | 479 | /@vueuse/shared/10.1.0_vue@3.2.47: 480 | resolution: {integrity: sha512-2X52ogu12i9DkKOQ01yeb/BKg9UO87RNnpm5sXkQvyORlbq8ONS5l39MYkjkeVWWjdT0teJru7a2S41dmHmqjQ==} 481 | dependencies: 482 | vue-demi: 0.14.0_vue@3.2.47 483 | transitivePeerDependencies: 484 | - '@vue/composition-api' 485 | - vue 486 | dev: false 487 | 488 | /ansi-colors/4.1.3: 489 | resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} 490 | engines: {node: '>=6'} 491 | dev: false 492 | 493 | /balanced-match/1.0.2: 494 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 495 | dev: true 496 | 497 | /base64-js/1.5.1: 498 | resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} 499 | dev: false 500 | 501 | /brace-expansion/2.0.1: 502 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 503 | dependencies: 504 | balanced-match: 1.0.2 505 | dev: true 506 | 507 | /buffer/6.0.3: 508 | resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} 509 | dependencies: 510 | base64-js: 1.5.1 511 | ieee754: 1.2.1 512 | dev: false 513 | 514 | /csstype/2.6.21: 515 | resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==} 516 | 517 | /de-indent/1.0.2: 518 | resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} 519 | dev: true 520 | 521 | /debug/4.3.4: 522 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 523 | engines: {node: '>=6.0'} 524 | peerDependencies: 525 | supports-color: '*' 526 | peerDependenciesMeta: 527 | supports-color: 528 | optional: true 529 | dependencies: 530 | ms: 2.1.2 531 | dev: false 532 | 533 | /err-code/3.0.1: 534 | resolution: {integrity: sha512-GiaH0KJUewYok+eeY05IIgjtAe4Yltygk9Wqp1V5yVWLdhf0hYZchRjNIT9bb0mSwRcIusT3cx7PJUf3zEIfUA==} 535 | dev: false 536 | 537 | /esbuild/0.17.17: 538 | resolution: {integrity: sha512-/jUywtAymR8jR4qsa2RujlAF7Krpt5VWi72Q2yuLD4e/hvtNcFQ0I1j8m/bxq238pf3/0KO5yuXNpuLx8BE1KA==} 539 | engines: {node: '>=12'} 540 | hasBin: true 541 | requiresBuild: true 542 | optionalDependencies: 543 | '@esbuild/android-arm': 0.17.17 544 | '@esbuild/android-arm64': 0.17.17 545 | '@esbuild/android-x64': 0.17.17 546 | '@esbuild/darwin-arm64': 0.17.17 547 | '@esbuild/darwin-x64': 0.17.17 548 | '@esbuild/freebsd-arm64': 0.17.17 549 | '@esbuild/freebsd-x64': 0.17.17 550 | '@esbuild/linux-arm': 0.17.17 551 | '@esbuild/linux-arm64': 0.17.17 552 | '@esbuild/linux-ia32': 0.17.17 553 | '@esbuild/linux-loong64': 0.17.17 554 | '@esbuild/linux-mips64el': 0.17.17 555 | '@esbuild/linux-ppc64': 0.17.17 556 | '@esbuild/linux-riscv64': 0.17.17 557 | '@esbuild/linux-s390x': 0.17.17 558 | '@esbuild/linux-x64': 0.17.17 559 | '@esbuild/netbsd-x64': 0.17.17 560 | '@esbuild/openbsd-x64': 0.17.17 561 | '@esbuild/sunos-x64': 0.17.17 562 | '@esbuild/win32-arm64': 0.17.17 563 | '@esbuild/win32-ia32': 0.17.17 564 | '@esbuild/win32-x64': 0.17.17 565 | dev: true 566 | 567 | /estree-walker/2.0.2: 568 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 569 | 570 | /flexsearch/0.7.21: 571 | resolution: {integrity: sha512-W7cHV7Hrwjid6lWmy0IhsWDFQboWSng25U3VVywpHOTJnnAZNPScog67G+cVpeX9f7yDD21ih0WDrMMT+JoaYg==} 572 | dev: false 573 | 574 | /fsevents/2.3.2: 575 | resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} 576 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 577 | os: [darwin] 578 | requiresBuild: true 579 | dev: true 580 | optional: true 581 | 582 | /get-browser-rtc/1.1.0: 583 | resolution: {integrity: sha512-MghbMJ61EJrRsDe7w1Bvqt3ZsBuqhce5nrn/XAwgwOXhcsz53/ltdxOse1h/8eKXj5slzxdsz56g5rzOFSGwfQ==} 584 | dev: false 585 | 586 | /he/1.2.0: 587 | resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} 588 | hasBin: true 589 | dev: true 590 | 591 | /idb-keyval/6.2.0: 592 | resolution: {integrity: sha512-uw+MIyQn2jl3+hroD7hF8J7PUviBU7BPKWw4f/ISf32D4LoGu98yHjrzWWJDASu9QNrX10tCJqk9YY0ClWm8Ng==} 593 | dependencies: 594 | safari-14-idb-fix: 3.0.0 595 | dev: false 596 | 597 | /ieee754/1.2.1: 598 | resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} 599 | dev: false 600 | 601 | /inherits/2.0.4: 602 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 603 | dev: false 604 | 605 | /is-hotkey/0.2.0: 606 | resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==} 607 | dev: false 608 | 609 | /isomorphic.js/0.2.5: 610 | resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==} 611 | dev: false 612 | 613 | /ky/0.33.3: 614 | resolution: {integrity: sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==} 615 | engines: {node: '>=14.16'} 616 | dev: false 617 | 618 | /lib0/0.2.73: 619 | resolution: {integrity: sha512-aJJIElCLWnHMcYZPtsM07QoSfHwpxCy4VUzBYGXFYEmh/h2QS5uZNbCCfL0CqnkOE30b7Tp9DVfjXag+3qzZjQ==} 620 | engines: {node: '>=14'} 621 | hasBin: true 622 | dependencies: 623 | isomorphic.js: 0.2.5 624 | dev: false 625 | 626 | /lru-cache/6.0.0: 627 | resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} 628 | engines: {node: '>=10'} 629 | dependencies: 630 | yallist: 4.0.0 631 | dev: true 632 | 633 | /magic-string/0.25.9: 634 | resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==} 635 | dependencies: 636 | sourcemap-codec: 1.4.8 637 | 638 | /merge/2.1.1: 639 | resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==} 640 | dev: false 641 | 642 | /minimatch/9.0.0: 643 | resolution: {integrity: sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w==} 644 | engines: {node: '>=16 || 14 >=14.17'} 645 | dependencies: 646 | brace-expansion: 2.0.1 647 | dev: true 648 | 649 | /ms/2.1.2: 650 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 651 | dev: false 652 | 653 | /muggle-string/0.2.2: 654 | resolution: {integrity: sha512-YVE1mIJ4VpUMqZObFndk9CJu6DBJR/GB13p3tXuNbwD4XExaI5EOuRl6BHeIDxIqXZVxSfAC+y6U1Z/IxCfKUg==} 655 | dev: true 656 | 657 | /nanoid/3.3.6: 658 | resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} 659 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 660 | hasBin: true 661 | 662 | /nanoid/4.0.2: 663 | resolution: {integrity: sha512-7ZtY5KTCNheRGfEFxnedV5zFiORN1+Y1N6zvPTnHQd8ENUvfaDBeuJDZb2bN/oXwXxu3qkTXDzy57W5vAmDTBw==} 664 | engines: {node: ^14 || ^16 || >=18} 665 | hasBin: true 666 | dev: false 667 | 668 | /picocolors/1.0.0: 669 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 670 | 671 | /postcss/8.4.23: 672 | resolution: {integrity: sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==} 673 | engines: {node: ^10 || ^12 || >=14} 674 | dependencies: 675 | nanoid: 3.3.6 676 | picocolors: 1.0.0 677 | source-map-js: 1.0.2 678 | 679 | /queue-microtask/1.2.3: 680 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 681 | dev: false 682 | 683 | /randombytes/2.1.0: 684 | resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} 685 | dependencies: 686 | safe-buffer: 5.2.1 687 | dev: false 688 | 689 | /readable-stream/3.6.2: 690 | resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} 691 | engines: {node: '>= 6'} 692 | dependencies: 693 | inherits: 2.0.4 694 | string_decoder: 1.3.0 695 | util-deprecate: 1.0.2 696 | dev: false 697 | 698 | /rollup/3.20.7: 699 | resolution: {integrity: sha512-P7E2zezKSLhWnTz46XxjSmInrbOCiul1yf+kJccMxT56vxjHwCbDfoLbiqFgu+WQoo9ij2PkraYaBstgB2prBA==} 700 | engines: {node: '>=14.18.0', npm: '>=8.0.0'} 701 | hasBin: true 702 | optionalDependencies: 703 | fsevents: 2.3.2 704 | dev: true 705 | 706 | /safari-14-idb-fix/3.0.0: 707 | resolution: {integrity: sha512-eBNFLob4PMq8JA1dGyFn6G97q3/WzNtFK4RnzT1fnLq+9RyrGknzYiM/9B12MnKAxuj1IXr7UKYtTNtjyKMBog==} 708 | dev: false 709 | 710 | /safe-buffer/5.2.1: 711 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 712 | dev: false 713 | 714 | /semver/7.5.0: 715 | resolution: {integrity: sha512-+XC0AD/R7Q2mPSRuy2Id0+CGTZ98+8f+KvwirxOKIEyid+XSx6HbC63p+O4IndTHuX5Z+JxQ0TghCkO5Cg/2HA==} 716 | engines: {node: '>=10'} 717 | hasBin: true 718 | dependencies: 719 | lru-cache: 6.0.0 720 | dev: true 721 | 722 | /simple-peer/9.11.1: 723 | resolution: {integrity: sha512-D1SaWpOW8afq1CZGWB8xTfrT3FekjQmPValrqncJMX7QFl8YwhrPTZvMCANLtgBwwdS+7zURyqxDDEmY558tTw==} 724 | dependencies: 725 | buffer: 6.0.3 726 | debug: 4.3.4 727 | err-code: 3.0.1 728 | get-browser-rtc: 1.1.0 729 | queue-microtask: 1.2.3 730 | randombytes: 2.1.0 731 | readable-stream: 3.6.2 732 | transitivePeerDependencies: 733 | - supports-color 734 | dev: false 735 | 736 | /source-map-js/1.0.2: 737 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 738 | engines: {node: '>=0.10.0'} 739 | 740 | /source-map/0.6.1: 741 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 742 | engines: {node: '>=0.10.0'} 743 | 744 | /sourcemap-codec/1.4.8: 745 | resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==} 746 | deprecated: Please use @jridgewell/sourcemap-codec instead 747 | 748 | /string_decoder/1.3.0: 749 | resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} 750 | dependencies: 751 | safe-buffer: 5.2.1 752 | dev: false 753 | 754 | /to-fast-properties/2.0.0: 755 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} 756 | engines: {node: '>=4'} 757 | 758 | /typescript/5.0.4: 759 | resolution: {integrity: sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==} 760 | engines: {node: '>=12.20'} 761 | hasBin: true 762 | 763 | /util-deprecate/1.0.2: 764 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 765 | dev: false 766 | 767 | /vite/4.3.1: 768 | resolution: {integrity: sha512-EPmfPLAI79Z/RofuMvkIS0Yr091T2ReUoXQqc5ppBX/sjFRhHKiPPF/R46cTdoci/XgeQpB23diiJxq5w30vdg==} 769 | engines: {node: ^14.18.0 || >=16.0.0} 770 | hasBin: true 771 | peerDependencies: 772 | '@types/node': '>= 14' 773 | less: '*' 774 | sass: '*' 775 | stylus: '*' 776 | sugarss: '*' 777 | terser: ^5.4.0 778 | peerDependenciesMeta: 779 | '@types/node': 780 | optional: true 781 | less: 782 | optional: true 783 | sass: 784 | optional: true 785 | stylus: 786 | optional: true 787 | sugarss: 788 | optional: true 789 | terser: 790 | optional: true 791 | dependencies: 792 | esbuild: 0.17.17 793 | postcss: 8.4.23 794 | rollup: 3.20.7 795 | optionalDependencies: 796 | fsevents: 2.3.2 797 | dev: true 798 | 799 | /vue-demi/0.14.0_vue@3.2.47: 800 | resolution: {integrity: sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg==} 801 | engines: {node: '>=12'} 802 | hasBin: true 803 | requiresBuild: true 804 | peerDependencies: 805 | '@vue/composition-api': ^1.0.0-rc.1 806 | vue: ^3.0.0-0 || ^2.6.0 807 | peerDependenciesMeta: 808 | '@vue/composition-api': 809 | optional: true 810 | dependencies: 811 | vue: 3.2.47 812 | dev: false 813 | 814 | /vue-template-compiler/2.7.14: 815 | resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==} 816 | dependencies: 817 | de-indent: 1.0.2 818 | he: 1.2.0 819 | dev: true 820 | 821 | /vue-tsc/1.4.2_typescript@5.0.4: 822 | resolution: {integrity: sha512-8VFjVekJuFtFG+N4rEimoR0OvNubhoTIMl2dlvbpyAD40LVPR1PN2SUc2qZPnWGGRsXZAVmFgiBHX0RB20HGyA==} 823 | hasBin: true 824 | peerDependencies: 825 | typescript: '*' 826 | dependencies: 827 | '@volar/vue-language-core': 1.4.2 828 | '@volar/vue-typescript': 1.4.2_typescript@5.0.4 829 | semver: 7.5.0 830 | typescript: 5.0.4 831 | dev: true 832 | 833 | /vue/3.2.47: 834 | resolution: {integrity: sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==} 835 | dependencies: 836 | '@vue/compiler-dom': 3.2.47 837 | '@vue/compiler-sfc': 3.2.47 838 | '@vue/runtime-dom': 3.2.47 839 | '@vue/server-renderer': 3.2.47_vue@3.2.47 840 | '@vue/shared': 3.2.47 841 | 842 | /ws/7.5.9: 843 | resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} 844 | engines: {node: '>=8.3.0'} 845 | requiresBuild: true 846 | peerDependencies: 847 | bufferutil: ^4.0.1 848 | utf-8-validate: ^5.0.2 849 | peerDependenciesMeta: 850 | bufferutil: 851 | optional: true 852 | utf-8-validate: 853 | optional: true 854 | dev: false 855 | optional: true 856 | 857 | /y-protocols/1.0.5: 858 | resolution: {integrity: sha512-Wil92b7cGk712lRHDqS4T90IczF6RkcvCwAD0A2OPg+adKmOe+nOiT/N2hvpQIWS3zfjmtL4CPaH5sIW1Hkm/A==} 859 | dependencies: 860 | lib0: 0.2.73 861 | dev: false 862 | 863 | /y-webrtc/10.2.5: 864 | resolution: {integrity: sha512-ZyBNvTI5L28sQ2PQI0T/JvyWgvuTq05L21vGkIlcvNLNSJqAaLCBJRe3FHEqXoaogqWmRcEAKGfII4ErNXMnNw==} 865 | engines: {node: '>=12'} 866 | hasBin: true 867 | dependencies: 868 | lib0: 0.2.73 869 | simple-peer: 9.11.1 870 | y-protocols: 1.0.5 871 | optionalDependencies: 872 | ws: 7.5.9 873 | transitivePeerDependencies: 874 | - bufferutil 875 | - supports-color 876 | - utf-8-validate 877 | dev: false 878 | 879 | /yallist/4.0.0: 880 | resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} 881 | dev: true 882 | 883 | /yjs/13.5.53: 884 | resolution: {integrity: sha512-B4UUycEK8BcYf195HL4LN4Az4Sg2+QzTHnabFHjQwLvGn96v/G+4CS52xNZk/0QWNXhLRCb+2GK3JmcX5fiCEQ==} 885 | engines: {node: '>=16.0.0', npm: '>=8.0.0'} 886 | dependencies: 887 | lib0: 0.2.73 888 | dev: false 889 | 890 | /zod/3.21.4: 891 | resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} 892 | dev: false 893 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'apps/*' 4 | - 'utils/*' 5 | --------------------------------------------------------------------------------