From 436d9de976104c382e817f6386f0a06f62839ba3 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Wed, 21 Oct 2026 17:38:25 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat(capture):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=85=B1=E4=BA=AB=E9=87=87=E9=9B=86=E5=86=85=E5=AE=B9=E7=9A=84?= =?UTF-8?q?=20Web=20=E8=AF=BB=E5=8F=96=E5=88=86=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 解释有序原文、原始图片、真实来源和独立附注 - 复用 Core Resolver、存储和内容导航,支持采集者停机后冷读 - 接入独立构建、包契约、Changeset 和隔离预览清单 --- .changeset/capture-reader.md | 5 + .github/preview/extensions.json | 5 + extensions/capture/README.md | 9 ++ extensions/capture/index.html | 12 +++ extensions/capture/package.json | 45 ++++++++ extensions/capture/src/AttachmentContent.vue | 28 +++++ extensions/capture/src/AttachmentPreview.vue | 12 +++ extensions/capture/src/CaptureContent.vue | 69 ++++++++++++ extensions/capture/src/CapturePreview.vue | 49 +++++++++ extensions/capture/src/DevApp.vue | 14 +++ extensions/capture/src/MaterialContent.vue | 18 ++++ extensions/capture/src/index.ts | 13 +++ extensions/capture/src/main.ts | 5 + extensions/capture/src/resolver.ts | 107 +++++++++++++++++++ extensions/capture/src/schema.ts | 44 ++++++++ extensions/capture/tsconfig.json | 14 +++ extensions/capture/vite.config.ts | 21 ++++ pnpm-lock.yaml | 84 +++++++++++++++ scripts/check-package-contract.mjs | 1 + scripts/check-workspace-contract.mjs | 6 ++ 20 files changed, 561 insertions(+) create mode 100644 .changeset/capture-reader.md create mode 100644 extensions/capture/README.md create mode 100644 extensions/capture/index.html create mode 100644 extensions/capture/package.json create mode 100644 extensions/capture/src/AttachmentContent.vue create mode 100644 extensions/capture/src/AttachmentPreview.vue create mode 100644 extensions/capture/src/CaptureContent.vue create mode 100644 extensions/capture/src/CapturePreview.vue create mode 100644 extensions/capture/src/DevApp.vue create mode 100644 extensions/capture/src/MaterialContent.vue create mode 100644 extensions/capture/src/index.ts create mode 100644 extensions/capture/src/main.ts create mode 100644 extensions/capture/src/resolver.ts create mode 100644 extensions/capture/src/schema.ts create mode 100644 extensions/capture/tsconfig.json create mode 100644 extensions/capture/vite.config.ts diff --git a/.changeset/capture-reader.md b/.changeset/capture-reader.md new file mode 100644 index 00000000..55260a38 --- /dev/null +++ b/.changeset/capture-reader.md @@ -0,0 +1,5 @@ +--- +'@inkcre/ext-capture': minor +--- + +新增随手收集分发,从共享信息库读取有序原文、原始图片、真实来源与用户附注。展示不依赖原 Mac 或 Python 执行 Peer 在线。 diff --git a/.github/preview/extensions.json b/.github/preview/extensions.json index 8ff23808..cadcc92d 100644 --- a/.github/preview/extensions.json +++ b/.github/preview/extensions.json @@ -6,6 +6,11 @@ "producer": "../../extensions/caldav/package.json", "artifact": "../../extensions/caldav/dist/client-web" }, + { + "kind": "module_federation", + "producer": "../../extensions/capture/package.json", + "artifact": "../../extensions/capture/dist/client-web" + }, { "kind": "module_federation", "producer": "../../extensions/twitter/package.json", diff --git a/extensions/capture/README.md b/extensions/capture/README.md new file mode 100644 index 00000000..913da96f --- /dev/null +++ b/extensions/capture/README.md @@ -0,0 +1,9 @@ +# Capture 读取分发 + +该原生 Module Federation 分发解释 `extensions.capture.capture.v1` 与 `extensions.capture.attachment.v1`,复用 Core 的文本、图片、存储和内容导航。它显示有序原材料、真实出处与单独的用户附注,不执行提交或依赖采集端在线。 + +Host SDK 要求 `@inkcre/core >=0.4.0 <0.5.0`。通过 Registry 选择并启用本浏览器的 `module_federation` 分发;`initialize` 注册 Resolver,`dispose` 只移除本分发拥有的类。Python 执行分发与本地读取分发分别参与生命周期。 + +业务图及请求合同由 `InKCre/impl-py/docs/30-unit-tdd/capture-extension.md` 维护;字段验证在 `src/schema.ts`,顺序和引用校验在 `src/resolver.ts`。缺失材料显示错误,不触发采集端重新抓取。来源 URL 只允许 HTTP(S) 作为可点击链接。 + +运行 `pnpm --filter @inkcre/ext-capture type-check` 与仓库 `pnpm build`。`dist/client-web` 是可重定位的发布制品,`src/main.ts` 只用于开发说明页。真实验收应安装生产构建,停止原采集者与执行者后冷启动打开共享根 Block,检查原文、图片字节、顺序和附注。 diff --git a/extensions/capture/index.html b/extensions/capture/index.html new file mode 100644 index 00000000..ec8a1181 --- /dev/null +++ b/extensions/capture/index.html @@ -0,0 +1,12 @@ + + + + + + 随手收集分发 + + +
+ + + diff --git a/extensions/capture/package.json b/extensions/capture/package.json new file mode 100644 index 00000000..c5ca81bc --- /dev/null +++ b/extensions/capture/package.json @@ -0,0 +1,45 @@ +{ + "name": "@inkcre/ext-capture", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "type-check": "vue-tsc --noEmit" + }, + "dependencies": { + "@inkcre/core": "workspace:*" + }, + "devDependencies": { + "@inkcre/ext-dev-utils": "workspace:*", + "@inkcre/ui-web": "2.2.1", + "@module-federation/vite": "^1.1.0", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/tsconfig": "^0.7.0", + "typescript": "catalog:", + "vite": "^7.0.6", + "vue-tsc": "catalog:" + }, + "peerDependencies": { + "@vueuse/core": "^14.0.0", + "pinia": "^3.0.0", + "vue": "^3.5.25", + "vue-router": "^4.5.0", + "zod": "^4.1.0" + }, + "inkcre": { + "name": "inkcre/capture", + "nickname": "随手收集", + "module_federation": { + "host_sdk": "@inkcre/core", + "host_sdk_version": ">=0.4.0 <0.5.0" + } + }, + "intent": { + "skills": [ + "@inkcre/ui-web" + ] + } +} diff --git a/extensions/capture/src/AttachmentContent.vue b/extensions/capture/src/AttachmentContent.vue new file mode 100644 index 00000000..ae2e9ed6 --- /dev/null +++ b/extensions/capture/src/AttachmentContent.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/extensions/capture/src/AttachmentPreview.vue b/extensions/capture/src/AttachmentPreview.vue new file mode 100644 index 00000000..b1d18477 --- /dev/null +++ b/extensions/capture/src/AttachmentPreview.vue @@ -0,0 +1,12 @@ + + diff --git a/extensions/capture/src/CaptureContent.vue b/extensions/capture/src/CaptureContent.vue new file mode 100644 index 00000000..02c8a12f --- /dev/null +++ b/extensions/capture/src/CaptureContent.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/extensions/capture/src/CapturePreview.vue b/extensions/capture/src/CapturePreview.vue new file mode 100644 index 00000000..9086796f --- /dev/null +++ b/extensions/capture/src/CapturePreview.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/extensions/capture/src/DevApp.vue b/extensions/capture/src/DevApp.vue new file mode 100644 index 00000000..ca963970 --- /dev/null +++ b/extensions/capture/src/DevApp.vue @@ -0,0 +1,14 @@ + + + diff --git a/extensions/capture/src/MaterialContent.vue b/extensions/capture/src/MaterialContent.vue new file mode 100644 index 00000000..dba8e312 --- /dev/null +++ b/extensions/capture/src/MaterialContent.vue @@ -0,0 +1,18 @@ + + + diff --git a/extensions/capture/src/index.ts b/extensions/capture/src/index.ts new file mode 100644 index 00000000..a7aef51c --- /dev/null +++ b/extensions/capture/src/index.ts @@ -0,0 +1,13 @@ +import { Resolver, type ExtensionModule } from '@inkcre/core' +import { CaptureResolver, AttachmentResolver } from './resolver' + +const resolvers = [CaptureResolver, AttachmentResolver] +const extension: ExtensionModule = { + async initialize() { + for (const resolver of resolvers) Resolver.register(resolver.type, resolver) + }, + async dispose() { + for (const resolver of resolvers) Resolver.unregister(resolver.type, resolver) + }, +} +export default extension diff --git a/extensions/capture/src/main.ts b/extensions/capture/src/main.ts new file mode 100644 index 00000000..c92ced8d --- /dev/null +++ b/extensions/capture/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import '@inkcre/ui-web/styles' +import DevApp from './DevApp.vue' + +createApp(DevApp).mount('#app') diff --git a/extensions/capture/src/resolver.ts b/extensions/capture/src/resolver.ts new file mode 100644 index 00000000..8754e01a --- /dev/null +++ b/extensions/capture/src/resolver.ts @@ -0,0 +1,107 @@ +import { markRaw } from 'vue' +import { Block, Resolver, ResolverCache, type ProjectionOptions } from '@inkcre/core' +import CaptureContent from './CaptureContent.vue' +import CapturePreview from './CapturePreview.vue' +import AttachmentContent from './AttachmentContent.vue' +import AttachmentPreview from './AttachmentPreview.vue' +import { + captureMetadata, + attachmentMetadata, + type SolvedCapture, + type SolvedAttachment, + type SolvedMaterial, +} from './schema' + +async function solve( + id: number, + allowed: string[], + options: ProjectionOptions +): Promise { + const block = await Block.find(id) + if (!block) throw new Error(`采集材料 Block ${id} 已不存在。`) + if (!allowed.includes(block.resolver)) throw new Error(`采集材料 Block ${id} 的格式不符合合同。`) + const resolver = await ResolverCache.getResolver(block) + return { + block, + resolver, + solvedContent: await resolver.getSolvedContent({ ...options, materializeMissing: false }), + } +} + +export class CaptureResolver extends Resolver { + static readonly type = 'extensions.capture.capture.v1' + static readonly previewRenderer = markRaw(CapturePreview) + static readonly solvedContentRenderer = markRaw(CaptureContent) + + protected override async _getSolvedContent(options: ProjectionOptions): Promise { + const metadata = captureMetadata.parse(JSON.parse(await this.getRawContent(options))) + const outgoing = await this.getRelations({ + includeIn: false, + includeOut: true, + refresh: options.refresh, + }) + const materials = outgoing.filter((relation) => relation.content.startsWith('material:')) + if (materials.some((relation) => !/^material:(0|[1-9]\d*)$/.test(relation.content))) { + throw new Error('采集材料顺序格式不正确。') + } + materials.sort((left, right) => Number(left.content.slice(9)) - Number(right.content.slice(9))) + if ( + materials.length > 128 || + materials.some((relation, index) => relation.content !== `material:${index}`) + ) { + throw new Error('采集材料顺序不连续或重复。') + } + const annotations = outgoing.filter((relation) => relation.content === 'annotation') + if (annotations.length > 1) throw new Error('采集内容存在重复附注。') + const result: SolvedCapture = { metadata, materials: [], annotation: null } + for (const relation of materials) { + result.materials.push( + await solve(relation.to_, ['core.text.v1', AttachmentResolver.type], options) + ) + } + if (annotations[0]) + result.annotation = await solve(annotations[0].to_, ['core.text.v1'], options) + return result + } + + override async getText(options: ProjectionOptions = {}): Promise { + const content = await this.getSolvedContent(options) + const origin = content.metadata.origin + const parts = [`采集时间:${content.metadata.captured_at}`, `入口:${content.metadata.entry}`] + if (origin?.title) parts.push(`来源标题:${origin.title}`) + if (origin?.url) parts.push(`来源位置:${origin.url}`) + if (origin?.application_id) parts.push(`来源应用:${origin.application_id}`) + for (const material of content.materials) { + const text = await material.resolver.getText(options) + if (text !== null) parts.push(`原材料:\n${text}`) + } + if (content.annotation) + parts.push(`用户附注:\n${await content.annotation.resolver.getText(options)}`) + return parts.join('\n\n') + } +} + +export class AttachmentResolver extends Resolver { + static readonly type = 'extensions.capture.attachment.v1' + static readonly previewRenderer = markRaw(AttachmentPreview) + static readonly solvedContentRenderer = markRaw(AttachmentContent) + + protected override async _getSolvedContent( + options: ProjectionOptions + ): Promise { + const metadata = attachmentMetadata.parse(JSON.parse(await this.getRawContent(options))) + const relations = await this.getRelations({ + includeIn: false, + includeOut: true, + refresh: options.refresh, + }) + const content = relations.filter((relation) => relation.content === 'content') + if (content.length !== 1 || !content[0]) throw new Error('图片附件必须引用一份原始内容。') + return { metadata, content: await solve(content[0].to_, ['core.image.v1'], options) } + } + + override async getText(options: ProjectionOptions = {}): Promise { + const content = await this.getSolvedContent(options) + return content.metadata.filename ?? `图片(${content.metadata.mime_type})` + } +} diff --git a/extensions/capture/src/schema.ts b/extensions/capture/src/schema.ts new file mode 100644 index 00000000..33ceb0f3 --- /dev/null +++ b/extensions/capture/src/schema.ts @@ -0,0 +1,44 @@ +import { z } from 'zod' +import type { Block, Resolver } from '@inkcre/core' + +export const captureMetadata = z.object({ + captured_at: z.iso.datetime({ offset: true }), + entry: z.enum(['selection', 'paste', 'screenshot', 'manual']), + origin: z + .object({ + application_id: z.string().nullable().optional(), + title: z.string().nullable().optional(), + url: z.string().nullable().optional(), + }) + .nullable() + .optional(), +}) +export const attachmentMetadata = z.object({ + mime_type: z.enum(['image/png', 'image/jpeg', 'image/gif', 'image/webp']), + filename: z.string().nullable().optional(), +}) + +export interface SolvedMaterial { + block: Block + resolver: Resolver + solvedContent: unknown +} +export interface SolvedCapture { + metadata: z.infer + materials: SolvedMaterial[] + annotation: SolvedMaterial | null +} +export interface SolvedAttachment { + metadata: z.infer + content: SolvedMaterial +} + +export function safeSourceURL(value: string | null | undefined): string | null { + if (!value) return null + try { + const url = new URL(value) + return ['https:', 'http:'].includes(url.protocol) ? url.href : null + } catch { + return null + } +} diff --git a/extensions/capture/tsconfig.json b/extensions/capture/tsconfig.json new file mode 100644 index 00000000..dc91eb8d --- /dev/null +++ b/extensions/capture/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "outDir": "./dist/types", + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo", + "experimentalDecorators": true, + "skipLibCheck": true, + "paths": { + "@inkcre/core": ["../../packages/core/src/index.ts"], + "@inkcre/core/*": ["../../packages/core/src/*"] + } + }, + "include": ["src/**/*", "src/**/*.vue"] +} diff --git a/extensions/capture/vite.config.ts b/extensions/capture/vite.config.ts new file mode 100644 index 00000000..9bdaecd3 --- /dev/null +++ b/extensions/capture/vite.config.ts @@ -0,0 +1,21 @@ +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { federation } from '@module-federation/vite' +import mfShared from '../mf-shared' +import extensionPackage from './package.json' + +export default defineConfig({ + plugins: [ + vue(), + federation({ + name: 'extension.capture', + filename: 'remoteEntry.js', + manifest: true, + exposes: { '.': fileURLToPath(new URL('./src/index.ts', import.meta.url)) }, + shared: mfShared(extensionPackage.inkcre.module_federation.host_sdk_version), + }), + ], + base: './', + build: { target: 'es2022', outDir: 'dist/client-web', sourcemap: true }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 409259af..ed9aac56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -357,6 +357,52 @@ importers: specifier: 'catalog:' version: 3.3.8(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2) + extensions/capture: + dependencies: + '@inkcre/core': + specifier: workspace:* + version: link:../../packages/core + '@vueuse/core': + specifier: ^14.0.0 + version: 14.4.0(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + pinia: + specifier: ^3.0.0 + version: 3.0.3(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + vue: + specifier: ^3.5.25 + version: 3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2) + vue-router: + specifier: ^4.5.0 + version: 4.5.1(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + zod: + specifier: ^4.1.0 + version: 4.4.3 + devDependencies: + '@inkcre/ext-dev-utils': + specifier: workspace:* + version: link:../../packages/ext-dev-utils + '@inkcre/ui-web': + specifier: 2.2.1 + version: 2.2.1(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + '@module-federation/vite': + specifier: ^1.1.0 + version: 1.7.1(rollup@4.59.0) + '@vitejs/plugin-vue': + specifier: ^6.0.1 + version: 6.0.3(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + '@vue/tsconfig': + specifier: ^0.7.0 + version: 0.7.0(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + typescript: + specifier: npm:typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 + version: typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 + vite: + specifier: ^7.0.6 + version: 7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0) + vue-tsc: + specifier: 'catalog:' + version: 3.3.8(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2) + extensions/github: dependencies: '@inkcre/core': @@ -1534,6 +1580,12 @@ packages: '@inkcre/core': '>=0.4.0 <0.5.0' '@module-federation/runtime': '>=0.21.4 <0.23.0' + '@inkcre/ui-web@2.2.1': + resolution: {integrity: sha512-rsljLVVzpzCRhywF0poVBFl21H48JAQwENwci+efL5/YOLjav9Uv7yh8oMN4eqmOiQw9Hgt4Tl6Epg2Rzb/g+w==, tarball: https://npm.pkg.github.com/download/@inkcre/ui-web/2.2.1/5b22fbb31e4bf02e4f742bb0f9353cacfb4b8fd9} + peerDependencies: + unocss: ^66.5.0 + vue: ^3.5.25 + '@inkcre/ui-web@https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz': resolution: {integrity: sha512-fXfAro12+zzyhtjiZqfQBvyMJKVnclrm/qvHst70dnRFZekYIkE+rSfabEJ//avAvvJyVZL7kK6XewXK4PgdJg==, tarball: https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz} version: 2.3.0 @@ -3096,6 +3148,17 @@ packages: '@vue/shared@3.5.40': resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==} + '@vue/tsconfig@0.7.0': + resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==} + peerDependencies: + typescript: 5.x + vue: ^3.4.0 + peerDependenciesMeta: + typescript: + optional: true + vue: + optional: true + '@vue/tsconfig@0.9.1': resolution: {integrity: sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==} peerDependencies: @@ -7026,6 +7089,22 @@ snapshots: semver: 7.8.5 zod: 4.1.12 + '@inkcre/ui-web@2.2.1(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2))': + dependencies: + '@codemirror/autocomplete': 6.20.3 + '@codemirror/commands': 6.11.1 + '@codemirror/lang-json': 6.0.2 + '@codemirror/lint': 6.9.7 + '@codemirror/state': 6.7.5 + '@codemirror/view': 6.43.12 + '@vue/shared': 3.5.25 + '@vueuse/core': 14.4.0(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + dayjs: 1.11.23 + unocss: 66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)) + vscode-json-languageservice: 5.6.4 + vscode-languageserver-textdocument: 1.0.14 + vue: 3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2) + '@inkcre/ui-web@https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2))': dependencies: '@codemirror/autocomplete': 6.20.3 @@ -8380,6 +8459,11 @@ snapshots: '@vue/shared@3.5.40': {} + '@vue/tsconfig@0.7.0(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2))': + optionalDependencies: + typescript: typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 + vue: 3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2) + '@vue/tsconfig@0.9.1(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2))': optionalDependencies: typescript: typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 diff --git a/scripts/check-package-contract.mjs b/scripts/check-package-contract.mjs index 278d46fc..6c6d4288 100644 --- a/scripts/check-package-contract.mjs +++ b/scripts/check-package-contract.mjs @@ -37,6 +37,7 @@ await Promise.all( 'apps/client-webext/.output/chrome-mv3/content-scripts/content.css', 'extensions/github/dist/client-web/remoteEntry.js', 'extensions/caldav/dist/client-web/remoteEntry.js', + 'extensions/capture/dist/client-web/remoteEntry.js', 'extensions/mail/dist/client-web/remoteEntry.js', 'extensions/memos/dist/client-web/remoteEntry.js', 'extensions/twitter/dist/client-web/remoteEntry.js', diff --git a/scripts/check-workspace-contract.mjs b/scripts/check-workspace-contract.mjs index 6990b7a8..cb724604 100644 --- a/scripts/check-workspace-contract.mjs +++ b/scripts/check-workspace-contract.mjs @@ -6,6 +6,12 @@ const repoRoot = fileURLToPath(new URL('..', import.meta.url)) const jsonOutput = process.argv.includes('--json') const workspaces = [ + { + path: 'extensions/capture', + name: '@inkcre/ext-capture', + requiredScripts: ['build', 'dev', 'type-check'], + builder: 'vite', + }, { path: 'extensions/caldav', name: '@inkcre/ext-caldav', From 866e86a518da9299caf1b8d00541b749b729f8d7 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Wed, 21 Oct 2026 20:30:13 +0800 Subject: [PATCH 2/8] =?UTF-8?q?docs(capture):=20=E6=98=8E=E7=A1=AE?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E5=88=86=E5=8F=91=E4=B8=8D=E6=98=AF=20Mac=20?= =?UTF-8?q?=E5=86=99=E5=85=A5=E5=89=8D=E6=8F=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 引用共享图合同,移除 Python 执行者依赖说明。 --- extensions/capture/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions/capture/README.md b/extensions/capture/README.md index 913da96f..41808276 100644 --- a/extensions/capture/README.md +++ b/extensions/capture/README.md @@ -4,6 +4,8 @@ Host SDK 要求 `@inkcre/core >=0.4.0 <0.5.0`。通过 Registry 选择并启用本浏览器的 `module_federation` 分发;`initialize` 注册 Resolver,`dispose` 只移除本分发拥有的类。Python 执行分发与本地读取分发分别参与生命周期。 -业务图及请求合同由 `InKCre/impl-py/docs/30-unit-tdd/capture-extension.md` 维护;字段验证在 `src/schema.ts`,顺序和引用校验在 `src/resolver.ts`。缺失材料显示错误,不触发采集端重新抓取。来源 URL 只允许 HTTP(S) 作为可点击链接。 +共享图与提交语义由 [主动采集持久化契约](https://github.com/InKCre/docs/blob/main/20-product-tdd/capture-contract.md) 维护;字段验证在 `src/schema.ts`,顺序和引用校验在 `src/resolver.ts`。缺失材料显示错误,不触发采集端重新抓取。来源 URL 只允许 HTTP(S) 作为可点击链接。 -运行 `pnpm --filter @inkcre/ext-capture type-check` 与仓库 `pnpm build`。`dist/client-web` 是可重定位的发布制品,`src/main.ts` 只用于开发说明页。真实验收应安装生产构建,停止原采集者与执行者后冷启动打开共享根 Block,检查原文、图片字节、顺序和附注。 +运行 `pnpm --filter @inkcre/ext-capture type-check` 与仓库 `pnpm build`。`dist/client-web` 是可重定位的发布制品,`src/main.ts` 只用于开发说明页。真实验收应安装生产构建,停止原采集者后冷启动打开共享根 Block,检查原文、图片字节、顺序和附注。 + +Mac 自行通过共享数据库原子写入,不依赖 Python Peer。此 Web 分发只负责读取与呈现,未启用它不影响采集写入。 From 4ba2a427ef8267a746b98d1752e831f5b3c29043 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Wed, 21 Oct 2026 22:55:33 +0800 Subject: [PATCH 3/8] =?UTF-8?q?fix(extension):=20=E8=AF=86=E5=88=AB?= =?UTF-8?q?=E9=9A=8F=E5=BA=94=E7=94=A8=E4=BA=A4=E4=BB=98=E7=9A=84=20Swift?= =?UTF-8?q?=20=E5=88=86=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 解释原生参与身份并阻止不受支持的 Registry 版本请求 - 随 Capture 读取产物交付 MIT 许可和发布意图 --- .changeset/capture-reader.md | 4 ++++ apps/client-web/src/extension-peer-control.ts | 2 ++ apps/client-web/src/locales/messages/en.json | 3 ++- .../src/locales/messages/zh-CN.json | 3 ++- .../src/views/extensions/extensions.vue | 3 +-- extensions/capture/LICENSE | 21 +++++++++++++++++++ extensions/capture/README.md | 4 ++-- extensions/capture/package.json | 1 + extensions/capture/vite.config.ts | 11 ++++++++++ packages/core/src/extension/model.ts | 2 +- 10 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 extensions/capture/LICENSE diff --git a/.changeset/capture-reader.md b/.changeset/capture-reader.md index 55260a38..113115ef 100644 --- a/.changeset/capture-reader.md +++ b/.changeset/capture-reader.md @@ -1,5 +1,9 @@ --- '@inkcre/ext-capture': minor +'@inkcre/core': patch +'@inkcre/client-web': patch --- 新增随手收集分发,从共享信息库读取有序原文、原始图片、真实来源与用户附注。展示不依赖原 Mac 或 Python 执行 Peer 在线。 + +Core 与 Web 可解释随签名应用交付的 swift_bundled 参与身份;版本管理提示转到对应应用,不调用不支持该格式的 Registry 接口。 diff --git a/apps/client-web/src/extension-peer-control.ts b/apps/client-web/src/extension-peer-control.ts index bb604716..8b4d4cc1 100644 --- a/apps/client-web/src/extension-peer-control.ts +++ b/apps/client-web/src/extension-peer-control.ts @@ -97,6 +97,8 @@ export async function selectExtensionPeerVersion(input: { livePeerIds: ReadonlySet manager: ExtensionManager }): Promise { + if (input.distribution === 'swift_bundled') + throw new Error('此分发随应用交付,请在对应 Peer 管理。') const mode = extensionPeerControlMode(input.peer, input.currentPeerId, input.livePeerIds) if (mode === 'current-runtime') { if (input.distribution !== 'module_federation') diff --git a/apps/client-web/src/locales/messages/en.json b/apps/client-web/src/locales/messages/en.json index 369c6ec8..e289e96c 100644 --- a/apps/client-web/src/locales/messages/en.json +++ b/apps/client-web/src/locales/messages/en.json @@ -255,7 +255,8 @@ "runtimeUnknown": "Runtime status unavailable", "unavailableOnPeer": "Unavailable on this Peer", "publishedDistributions": "Published distributions", - "noPublishedDistribution": "No published {distribution} releases." + "noPublishedDistribution": "No published {distribution} releases.", + "bundledManagedByApp": "This distribution ships with its application. Manage it and consult its documentation in that application, rather than Registry." }, "job": { "loadFailed": "Unable to load this job.", diff --git a/apps/client-web/src/locales/messages/zh-CN.json b/apps/client-web/src/locales/messages/zh-CN.json index 97f718cd..2a18dcd0 100644 --- a/apps/client-web/src/locales/messages/zh-CN.json +++ b/apps/client-web/src/locales/messages/zh-CN.json @@ -255,7 +255,8 @@ "runtimeUnknown": "运行状态未知", "unavailableOnPeer": "此 Peer 不可用", "publishedDistributions": "已发布分发", - "noPublishedDistribution": "尚无已发布的 {distribution} 版本。" + "noPublishedDistribution": "尚无已发布的 {distribution} 版本。", + "bundledManagedByApp": "此分发随应用交付,不通过 Registry 获取版本或文档。请在对应应用中管理。" }, "job": { "loadFailed": "无法读取此任务。", diff --git a/apps/client-web/src/views/extensions/extensions.vue b/apps/client-web/src/views/extensions/extensions.vue index 085141d8..48dcffc8 100644 --- a/apps/client-web/src/views/extensions/extensions.vue +++ b/apps/client-web/src/views/extensions/extensions.vue @@ -11,7 +11,6 @@ import { PeerManager, ExtensionParticipationModel, type ExtensionParticipation, - type NativeDistribution, type InstalledExtension, } from '@inkcre/core' import type { ReleaseRecord } from '@inkcre/extension-runtime-client-web' @@ -37,7 +36,7 @@ const installPeers = ref([]) const installPeerOptions = computed(() => installPeers.value.map((peer) => ({ label: peer.name, value: peer.id })) ) -const installDistribution = computed(() => +const installDistribution = computed<'python' | 'module_federation'>(() => route.query.distribution === 'python' ? 'python' : 'module_federation' ) const peersLoading = ref(false) diff --git a/extensions/capture/LICENSE b/extensions/capture/LICENSE new file mode 100644 index 00000000..94e962af --- /dev/null +++ b/extensions/capture/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 InKCre contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extensions/capture/README.md b/extensions/capture/README.md index 41808276..414ec300 100644 --- a/extensions/capture/README.md +++ b/extensions/capture/README.md @@ -2,9 +2,9 @@ 该原生 Module Federation 分发解释 `extensions.capture.capture.v1` 与 `extensions.capture.attachment.v1`,复用 Core 的文本、图片、存储和内容导航。它显示有序原材料、真实出处与单独的用户附注,不执行提交或依赖采集端在线。 -Host SDK 要求 `@inkcre/core >=0.4.0 <0.5.0`。通过 Registry 选择并启用本浏览器的 `module_federation` 分发;`initialize` 注册 Resolver,`dispose` 只移除本分发拥有的类。Python 执行分发与本地读取分发分别参与生命周期。 +Host SDK 要求 `@inkcre/core >=0.4.0 <0.5.0`。通过 Registry 选择并启用本浏览器的 `module_federation` 分发;`initialize` 注册 Resolver,`dispose` 只移除本分发拥有的类。Swift、Python 与 Web 分发按各自能力对等参与同一 Extension。 -共享图与提交语义由 [主动采集持久化契约](https://github.com/InKCre/docs/blob/main/20-product-tdd/capture-contract.md) 维护;字段验证在 `src/schema.ts`,顺序和引用校验在 `src/resolver.ts`。缺失材料显示错误,不触发采集端重新抓取。来源 URL 只允许 HTTP(S) 作为可点击链接。 +共享图与提交语义由 [Capture Extension 合同](https://github.com/InKCre/docs/blob/main/20-product-tdd/capture-contract.md) 维护;字段验证在 `src/schema.ts`,顺序和引用校验在 `src/resolver.ts`。缺失材料显示错误,不触发采集端重新抓取。来源 URL 只允许 HTTP(S) 作为可点击链接。 运行 `pnpm --filter @inkcre/ext-capture type-check` 与仓库 `pnpm build`。`dist/client-web` 是可重定位的发布制品,`src/main.ts` 只用于开发说明页。真实验收应安装生产构建,停止原采集者后冷启动打开共享根 Block,检查原文、图片字节、顺序和附注。 diff --git a/extensions/capture/package.json b/extensions/capture/package.json index c5ca81bc..8cc4abe5 100644 --- a/extensions/capture/package.json +++ b/extensions/capture/package.json @@ -2,6 +2,7 @@ "name": "@inkcre/ext-capture", "version": "0.1.0", "private": true, + "license": "MIT", "type": "module", "scripts": { "dev": "vite", diff --git a/extensions/capture/vite.config.ts b/extensions/capture/vite.config.ts index 9bdaecd3..b26ef9db 100644 --- a/extensions/capture/vite.config.ts +++ b/extensions/capture/vite.config.ts @@ -1,3 +1,4 @@ +import { readFileSync } from 'node:fs' import { fileURLToPath } from 'node:url' import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' @@ -8,6 +9,16 @@ import extensionPackage from './package.json' export default defineConfig({ plugins: [ vue(), + { + name: 'capture-license', + generateBundle() { + this.emitFile({ + type: 'asset', + fileName: 'LICENSE', + source: readFileSync(new URL('./LICENSE', import.meta.url), 'utf8'), + }) + }, + }, federation({ name: 'extension.capture', filename: 'remoteEntry.js', diff --git a/packages/core/src/extension/model.ts b/packages/core/src/extension/model.ts index d4407ca4..364f5985 100644 --- a/packages/core/src/extension/model.ts +++ b/packages/core/src/extension/model.ts @@ -22,7 +22,7 @@ export const ExtensionVersionSchema = z.string().refine( { error: 'Extension version must be strict SemVer without build metadata.' } ) -export const NativeDistributionSchema = z.enum(['python', 'module_federation']) +export const NativeDistributionSchema = z.enum(['python', 'module_federation', 'swift_bundled']) export type NativeDistribution = z.infer /** Shared identity and configuration; native versions belong to individual Peers. */ From 09bcb7f96fb5b5808f6f0b5115a71151478573dc Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Fri, 23 Oct 2026 18:24:48 +0800 Subject: [PATCH 4/8] =?UTF-8?q?feat(local-files):=20=E6=8F=90=E4=BE=9B=20W?= =?UTF-8?q?eb=20=E6=96=87=E4=BB=B6=E6=AD=A3=E6=96=87=E8=AF=BB=E5=8F=96?= =?UTF-8?q?=E5=88=86=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 展示已收入正文、原件位置、失效状态与提取范围 - 只注册读取能力,保留可重定位 Module Federation 产物 - 未发布版本仅记录 patch 发布意图 --- .changeset/local-files-reader.md | 5 ++ extensions/local-files/LICENSE | 21 +++++++++ extensions/local-files/README.md | 5 ++ extensions/local-files/index.html | 12 +++++ extensions/local-files/package.json | 46 ++++++++++++++++++ extensions/local-files/src/DevApp.vue | 12 +++++ extensions/local-files/src/FileContent.vue | 52 +++++++++++++++++++++ extensions/local-files/src/FilePreview.vue | 38 +++++++++++++++ extensions/local-files/src/index.ts | 13 ++++++ extensions/local-files/src/main.ts | 5 ++ extensions/local-files/src/resolver.ts | 43 +++++++++++++++++ extensions/local-files/src/schema.ts | 54 ++++++++++++++++++++++ extensions/local-files/tsconfig.json | 14 ++++++ extensions/local-files/vite.config.ts | 32 +++++++++++++ pnpm-lock.yaml | 46 ++++++++++++++++++ scripts/check-package-contract.mjs | 1 + scripts/check-workspace-contract.mjs | 6 +++ scripts/tests/local-files-contract.test.ts | 37 +++++++++++++++ 18 files changed, 442 insertions(+) create mode 100644 .changeset/local-files-reader.md create mode 100644 extensions/local-files/LICENSE create mode 100644 extensions/local-files/README.md create mode 100644 extensions/local-files/index.html create mode 100644 extensions/local-files/package.json create mode 100644 extensions/local-files/src/DevApp.vue create mode 100644 extensions/local-files/src/FileContent.vue create mode 100644 extensions/local-files/src/FilePreview.vue create mode 100644 extensions/local-files/src/index.ts create mode 100644 extensions/local-files/src/main.ts create mode 100644 extensions/local-files/src/resolver.ts create mode 100644 extensions/local-files/src/schema.ts create mode 100644 extensions/local-files/tsconfig.json create mode 100644 extensions/local-files/vite.config.ts create mode 100644 scripts/tests/local-files-contract.test.ts diff --git a/.changeset/local-files-reader.md b/.changeset/local-files-reader.md new file mode 100644 index 00000000..f7eba9e1 --- /dev/null +++ b/.changeset/local-files-reader.md @@ -0,0 +1,5 @@ +--- +'@inkcre/ext-local-files': patch +--- + +读取本地文件资料及已提取正文,展示原件失效与提取范围。 diff --git a/extensions/local-files/LICENSE b/extensions/local-files/LICENSE new file mode 100644 index 00000000..94e962af --- /dev/null +++ b/extensions/local-files/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 InKCre contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extensions/local-files/README.md b/extensions/local-files/README.md new file mode 100644 index 00000000..e5431ffd --- /dev/null +++ b/extensions/local-files/README.md @@ -0,0 +1,5 @@ +# 本地文件读取分发 + +解释 `inkcre/local-files` 的文件资料与 `body` 正文,展示 Peer 位置和原件状态。原件位置仅为文字,不将自定义 scheme 交给浏览器或远程取文件。目录监视与扫描 Job 由具备本机文件访问能力的分发执行;此包只注册 Resolver。 + +共享合同见 InKCre/docs 的 `20-product-tdd/local-files-contract.md`。使用 `pnpm --filter @inkcre/ext-local-files build` 构建可重定位的 Module Federation 分发。 diff --git a/extensions/local-files/index.html b/extensions/local-files/index.html new file mode 100644 index 00000000..77e34b00 --- /dev/null +++ b/extensions/local-files/index.html @@ -0,0 +1,12 @@ + + + + + + 本地文件分发 + + +
+ + + diff --git a/extensions/local-files/package.json b/extensions/local-files/package.json new file mode 100644 index 00000000..374eb5b7 --- /dev/null +++ b/extensions/local-files/package.json @@ -0,0 +1,46 @@ +{ + "name": "@inkcre/ext-local-files", + "version": "0.1.0", + "private": true, + "license": "MIT", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview", + "type-check": "vue-tsc --noEmit" + }, + "dependencies": { + "@inkcre/core": "workspace:*" + }, + "devDependencies": { + "@inkcre/ext-dev-utils": "workspace:*", + "@inkcre/ui-web": "2.2.1", + "@module-federation/vite": "^1.1.0", + "@vitejs/plugin-vue": "^6.0.1", + "@vue/tsconfig": "^0.7.0", + "typescript": "catalog:", + "vite": "^7.0.6", + "vue-tsc": "catalog:" + }, + "peerDependencies": { + "@vueuse/core": "^14.0.0", + "pinia": "^3.0.0", + "vue": "^3.5.25", + "vue-router": "^4.5.0", + "zod": "^4.1.0" + }, + "inkcre": { + "name": "inkcre/local-files", + "nickname": "本地文件", + "module_federation": { + "host_sdk": "@inkcre/core", + "host_sdk_version": ">=0.4.0 <0.5.0" + } + }, + "intent": { + "skills": [ + "@inkcre/ui-web" + ] + } +} diff --git a/extensions/local-files/src/DevApp.vue b/extensions/local-files/src/DevApp.vue new file mode 100644 index 00000000..52c033b7 --- /dev/null +++ b/extensions/local-files/src/DevApp.vue @@ -0,0 +1,12 @@ + + diff --git a/extensions/local-files/src/FileContent.vue b/extensions/local-files/src/FileContent.vue new file mode 100644 index 00000000..c5f5f17d --- /dev/null +++ b/extensions/local-files/src/FileContent.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/extensions/local-files/src/FilePreview.vue b/extensions/local-files/src/FilePreview.vue new file mode 100644 index 00000000..d8094995 --- /dev/null +++ b/extensions/local-files/src/FilePreview.vue @@ -0,0 +1,38 @@ + + + diff --git a/extensions/local-files/src/index.ts b/extensions/local-files/src/index.ts new file mode 100644 index 00000000..fc4928bb --- /dev/null +++ b/extensions/local-files/src/index.ts @@ -0,0 +1,13 @@ +import { Resolver, type ExtensionModule } from '@inkcre/core' +import { LocalFileResolver } from './resolver' + +const resolvers = [LocalFileResolver] +const extension: ExtensionModule = { + async initialize() { + for (const resolver of resolvers) Resolver.register(resolver.type, resolver) + }, + async dispose() { + for (const resolver of resolvers) Resolver.unregister(resolver.type, resolver) + }, +} +export default extension diff --git a/extensions/local-files/src/main.ts b/extensions/local-files/src/main.ts new file mode 100644 index 00000000..c92ced8d --- /dev/null +++ b/extensions/local-files/src/main.ts @@ -0,0 +1,5 @@ +import { createApp } from 'vue' +import '@inkcre/ui-web/styles' +import DevApp from './DevApp.vue' + +createApp(DevApp).mount('#app') diff --git a/extensions/local-files/src/resolver.ts b/extensions/local-files/src/resolver.ts new file mode 100644 index 00000000..87dd431c --- /dev/null +++ b/extensions/local-files/src/resolver.ts @@ -0,0 +1,43 @@ +import { markRaw } from 'vue' +import { Block, Resolver, type ProjectionOptions } from '@inkcre/core' +import { fileMetadata, type SolvedFile } from './schema' +import FileContent from './FileContent.vue' +import FilePreview from './FilePreview.vue' + +export class LocalFileResolver extends Resolver { + static readonly type = 'extensions.local_files.file.v1' + static readonly previewRenderer = markRaw(FilePreview) + static readonly solvedContentRenderer = markRaw(FileContent) + + protected override async _getSolvedContent(options: ProjectionOptions): Promise { + const metadata = fileMetadata.parse(JSON.parse(await this.getRawContent(options))) + const relations = await this.getRelations({ + includeIn: false, + includeOut: true, + refresh: options.refresh, + }) + const bodies = relations.filter((item) => item.content === 'body') + if (bodies.length > 1 || (metadata.revision && !bodies.length)) + throw new Error('文件资料的正文关系无效。') + const body = bodies[0] + if (!body) return { metadata, text: null, bodyID: null } + const block = await Block.find(body.to_) + if (!block || block.resolver !== 'core.text.v1' || block.storage !== null) + throw new Error('文件正文不存在或类型不符。') + return { metadata, text: block.content, bodyID: block.id } + } + + override async getText(options: ProjectionOptions = {}): Promise { + const { metadata, text } = await this.getSolvedContent({ + ...options, + materializeMissing: false, + }) + const parts = [`原件位置:${metadata.location}`] + if (!metadata.available) parts.push('原件已失效;保留最后收入的正文。') + if (metadata.issue) parts.push(`当前提取失败:${metadata.issue}。正文对应上次成功修订。`) + if (metadata.revision?.scope === 'main_document') + parts.push('DOCX 主文档文字,不含脚注、批注。') + if (text !== null) parts.push(text) + return parts.join('\n\n') + } +} diff --git a/extensions/local-files/src/schema.ts b/extensions/local-files/src/schema.ts new file mode 100644 index 00000000..a74a89fb --- /dev/null +++ b/extensions/local-files/src/schema.ts @@ -0,0 +1,54 @@ +import { z } from 'zod' + +export function peerFileLocation(value: string): { peer: string; path: string } { + const url = new URL(value) + if ( + url.protocol !== 'inkcre-peer-file:' || + url.username || + url.password || + url.port || + value.includes('?') || + value.includes('#') + ) + throw new Error('原件位置格式无效。') + const peer = z.uuid().parse(url.hostname) + const path = decodeURIComponent(url.pathname) + if (!path.startsWith('/') || path.includes('\0')) throw new Error('原件位置需要绝对路径。') + return { peer, path } +} + +export const fileMetadata = z.strictObject({ + location: z.string().refine((value) => { + try { + peerFileLocation(value) + return true + } catch { + return false + } + }), + format: z.enum(['pdf', 'docx', 'markdown', 'org', 'html', 'text']), + identity: z + .strictObject({ + volume: z.string(), + number: z.number().int().nonnegative(), + created: z.iso.datetime(), + }) + .optional(), + available: z.boolean(), + observed_at: z.iso.datetime(), + revision: z + .strictObject({ + sha256: z.string().regex(/^[0-9a-f]{64}$/), + extractor: z.string(), + extracted_at: z.iso.datetime(), + scope: z.enum(['text', 'text_layer', 'main_document']), + }) + .optional(), + issue: z.string().optional(), +}) + +export type SolvedFile = { + metadata: z.infer + text: string | null + bodyID: number | null +} diff --git a/extensions/local-files/tsconfig.json b/extensions/local-files/tsconfig.json new file mode 100644 index 00000000..dc91eb8d --- /dev/null +++ b/extensions/local-files/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "compilerOptions": { + "outDir": "./dist/types", + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo", + "experimentalDecorators": true, + "skipLibCheck": true, + "paths": { + "@inkcre/core": ["../../packages/core/src/index.ts"], + "@inkcre/core/*": ["../../packages/core/src/*"] + } + }, + "include": ["src/**/*", "src/**/*.vue"] +} diff --git a/extensions/local-files/vite.config.ts b/extensions/local-files/vite.config.ts new file mode 100644 index 00000000..205069a1 --- /dev/null +++ b/extensions/local-files/vite.config.ts @@ -0,0 +1,32 @@ +import { readFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import { federation } from '@module-federation/vite' +import mfShared from '../mf-shared' +import extensionPackage from './package.json' + +export default defineConfig({ + plugins: [ + vue(), + { + name: 'local-files-license', + generateBundle() { + this.emitFile({ + type: 'asset', + fileName: 'LICENSE', + source: readFileSync(new URL('./LICENSE', import.meta.url), 'utf8'), + }) + }, + }, + federation({ + name: 'extension.localFiles', + filename: 'remoteEntry.js', + manifest: true, + exposes: { '.': fileURLToPath(new URL('./src/index.ts', import.meta.url)) }, + shared: mfShared(extensionPackage.inkcre.module_federation.host_sdk_version), + }), + ], + base: './', + build: { target: 'es2022', outDir: 'dist/client-web', sourcemap: true }, +}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ed9aac56..51ab78d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -452,6 +452,52 @@ importers: specifier: ^4.1.12 version: 4.4.3 + extensions/local-files: + dependencies: + '@inkcre/core': + specifier: workspace:* + version: link:../../packages/core + '@vueuse/core': + specifier: ^14.0.0 + version: 14.4.0(vue@3.5.40(typescript@5.9.3)) + pinia: + specifier: ^3.0.0 + version: 3.0.3(typescript@5.9.3)(vue@3.5.40(typescript@5.9.3)) + vue: + specifier: ^3.5.25 + version: 3.5.40(typescript@5.9.3) + vue-router: + specifier: ^4.5.0 + version: 4.5.1(vue@3.5.40(typescript@5.9.3)) + zod: + specifier: ^4.1.0 + version: 4.4.3 + devDependencies: + '@inkcre/ext-dev-utils': + specifier: workspace:* + version: link:../../packages/ext-dev-utils + '@inkcre/ui-web': + specifier: 2.2.1 + version: 2.2.1(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript@5.9.3)) + '@module-federation/vite': + specifier: ^1.1.0 + version: 1.7.1(rollup@4.59.0) + '@vitejs/plugin-vue': + specifier: ^6.0.1 + version: 6.0.3(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + '@vue/tsconfig': + specifier: ^0.7.0 + version: 0.7.0(typescript@5.9.3)(vue@3.5.40(typescript@5.9.3)) + typescript: + specifier: 'catalog:' + version: 5.9.3 + vite: + specifier: ^7.0.6 + version: 7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0) + vue-tsc: + specifier: 'catalog:' + version: 3.3.8(typescript@5.9.3) + extensions/mail: dependencies: '@inkcre/core': diff --git a/scripts/check-package-contract.mjs b/scripts/check-package-contract.mjs index 6c6d4288..1bed95cf 100644 --- a/scripts/check-package-contract.mjs +++ b/scripts/check-package-contract.mjs @@ -38,6 +38,7 @@ await Promise.all( 'extensions/github/dist/client-web/remoteEntry.js', 'extensions/caldav/dist/client-web/remoteEntry.js', 'extensions/capture/dist/client-web/remoteEntry.js', + 'extensions/local-files/dist/client-web/remoteEntry.js', 'extensions/mail/dist/client-web/remoteEntry.js', 'extensions/memos/dist/client-web/remoteEntry.js', 'extensions/twitter/dist/client-web/remoteEntry.js', diff --git a/scripts/check-workspace-contract.mjs b/scripts/check-workspace-contract.mjs index cb724604..76d0ac8f 100644 --- a/scripts/check-workspace-contract.mjs +++ b/scripts/check-workspace-contract.mjs @@ -6,6 +6,12 @@ const repoRoot = fileURLToPath(new URL('..', import.meta.url)) const jsonOutput = process.argv.includes('--json') const workspaces = [ + { + path: 'extensions/local-files', + name: '@inkcre/ext-local-files', + requiredScripts: ['build', 'dev', 'type-check'], + builder: 'vite', + }, { path: 'extensions/capture', name: '@inkcre/ext-capture', diff --git a/scripts/tests/local-files-contract.test.ts b/scripts/tests/local-files-contract.test.ts new file mode 100644 index 00000000..e6f6c659 --- /dev/null +++ b/scripts/tests/local-files-contract.test.ts @@ -0,0 +1,37 @@ +import assert from 'node:assert/strict' +import test from 'node:test' +import { fileMetadata, peerFileLocation } from '../../extensions/local-files/src/schema.ts' + +test('Peer file location preserves Unicode and reserved path characters', () => { + const result = peerFileLocation( + 'inkcre-peer-file://a143b185-f43d-47d1-8ef7-1d3857284b01/Users/a/%E4%B8%AD%E6%96%87%20%23%3F.md' + ) + assert.equal(result.path, '/Users/a/中文 #?.md') + for (const invalid of [ + 'file:///Users/a', + 'inkcre-peer-file://bad-peer/a', + 'inkcre-peer-file://a143b185-f43d-47d1-8ef7-1d3857284b01/a#', + ]) { + assert.throws(() => peerFileLocation(invalid)) + } +}) + +test('Missing originals and failed extraction retain a distinct successful revision', () => { + const metadata = fileMetadata.parse({ + location: 'inkcre-peer-file://a143b185-f43d-47d1-8ef7-1d3857284b01/Users/a/report.docx', + format: 'docx', + available: false, + observed_at: '2026-10-23T06:00:00Z', + revision: { + sha256: 'a'.repeat(64), + extractor: 'mac-native-text.v1', + extracted_at: '2026-10-22T06:00:00Z', + scope: 'main_document', + }, + issue: '当前原件无法读取', + }) + assert.equal(metadata.available, false) + assert.equal(metadata.revision?.scope, 'main_document') + assert.equal(metadata.revision?.extracted_at, '2026-10-22T06:00:00Z') + assert.equal(fileMetadata.safeParse({ ...metadata, format: 'executable' }).success, false) +}) From 4c498dc162fe73c914a28f90a5f2d7b8824fdf22 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Fri, 23 Oct 2026 18:26:27 +0800 Subject: [PATCH 5/8] =?UTF-8?q?docs(shared):=20=E5=AF=B9=E9=BD=90=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E6=96=87=E4=BB=B6=E5=80=99=E9=80=89=E5=90=88=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 固定已推送的 Hub 合同提交 831e835 - 与分发实现提交分离,合并依赖 InKCre/docs#42 --- docs/_shared | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_shared b/docs/_shared index 7449135f..831e8358 160000 --- a/docs/_shared +++ b/docs/_shared @@ -1 +1 @@ -Subproject commit 7449135fefdd6056fecba387e59e3005aed5eafd +Subproject commit 831e8358a87894ecf29f25e18bff2c2ef3ce42bb From 76ddec26683f6eee007adca322cc69c4480482d0 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Mon, 26 Oct 2026 22:28:51 +0800 Subject: [PATCH 6/8] =?UTF-8?q?ref(extensions):=20=E5=BC=80=E6=94=BE=20Pee?= =?UTF-8?q?r=20=E5=88=86=E5=8F=91=E6=A0=87=E8=AF=86=E5=B9=B6=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E5=AE=BF=E4=B8=BB=E5=8A=A0=E8=BD=BD=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 共享状态和命令允许原生宿主格式,不扩充固定语言白名单。 - 读取 Capture 提交身份,Registry 安装操作仍只处理实际支持的产物。 --- .changeset/open-peer-distributions.md | 5 +++++ .../extensionCard/use-extension-info-dialogs.ts | 5 +++++ .../extensionCard/use-extension-peer-runtime.ts | 4 +++- .../extensionCard/use-extension-version-dialog.ts | 11 +++++++++-- apps/client-web/src/extension-peer-control.ts | 4 ++-- apps/client-web/src/locales/messages/en.json | 2 +- apps/client-web/src/locales/messages/zh-CN.json | 2 +- extensions/capture/src/schema.ts | 2 ++ packages/core/src/extension/model.ts | 2 +- 9 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 .changeset/open-peer-distributions.md diff --git a/.changeset/open-peer-distributions.md b/.changeset/open-peer-distributions.md new file mode 100644 index 00000000..ac2822fc --- /dev/null +++ b/.changeset/open-peer-distributions.md @@ -0,0 +1,5 @@ +--- +"@inkcre/core": patch +--- + +共享 Extension 参与与状态模型保留宿主的分发标识,移除固定语言/打包格式白名单。 diff --git a/apps/client-web/src/components/extension/extensionCard/use-extension-info-dialogs.ts b/apps/client-web/src/components/extension/extensionCard/use-extension-info-dialogs.ts index 8aa98ff0..250d2a2f 100644 --- a/apps/client-web/src/components/extension/extensionCard/use-extension-info-dialogs.ts +++ b/apps/client-web/src/components/extension/extensionCard/use-extension-info-dialogs.ts @@ -42,6 +42,11 @@ export function useExtensionInfoDialogs(options: { docsLoading.value = true documentation.value = [] docsError.value = '' + if (pin.distribution !== 'python' && pin.distribution !== 'module_federation') { + docsError.value = t('extension.managedByPeer') + docsLoading.value = false + return + } try { const origin = await new ExtensionRegistryOriginResolver( () => configStore.peerConfig.extension_registry_url diff --git a/apps/client-web/src/components/extension/extensionCard/use-extension-peer-runtime.ts b/apps/client-web/src/components/extension/extensionCard/use-extension-peer-runtime.ts index 32273d68..4533cbbf 100644 --- a/apps/client-web/src/components/extension/extensionCard/use-extension-peer-runtime.ts +++ b/apps/client-web/src/components/extension/extensionCard/use-extension-peer-runtime.ts @@ -234,7 +234,9 @@ export function useExtensionPeerRuntime(options: { ? 'Python' : distribution === 'module_federation' ? 'Module Federation' - : t('extension.distributionUnknown') + : distribution === 'swift_bundled' + ? 'Swift (bundled)' + : (distribution ?? t('extension.distributionUnknown')) } function supportedFormats(peer: Peer): NativeDistribution[] { diff --git a/apps/client-web/src/components/extension/extensionCard/use-extension-version-dialog.ts b/apps/client-web/src/components/extension/extensionCard/use-extension-version-dialog.ts index d29663e5..6f84c75f 100644 --- a/apps/client-web/src/components/extension/extensionCard/use-extension-version-dialog.ts +++ b/apps/client-web/src/components/extension/extensionCard/use-extension-version-dialog.ts @@ -39,7 +39,7 @@ export function useExtensionVersionDialog(options: { const distributionOptions = computed(() => selectedPeer.value ? runtime.supportedFormats(selectedPeer.value).map((format) => ({ - label: format === 'python' ? 'Python' : 'Module Federation', + label: runtime.distributionLabel(format), value: format, })) : [] @@ -68,7 +68,14 @@ export function useExtensionVersionDialog(options: { versionLoading.value = true versionOptions.value = [] versionError.value = '' - const readPublished = async (format: NativeDistribution): Promise => { + if (distribution !== 'python' && distribution !== 'module_federation') { + versionError.value = t('extension.managedByPeer') + versionLoading.value = false + return + } + const readPublished = async ( + format: 'python' | 'module_federation' + ): Promise => { try { const catalog = await getExtensionRegistry().getExtension(data.extension.name, format) const releases = sortPublishedReleases(catalog.releases ?? []) diff --git a/apps/client-web/src/extension-peer-control.ts b/apps/client-web/src/extension-peer-control.ts index 8b4d4cc1..5c9bdf90 100644 --- a/apps/client-web/src/extension-peer-control.ts +++ b/apps/client-web/src/extension-peer-control.ts @@ -97,8 +97,8 @@ export async function selectExtensionPeerVersion(input: { livePeerIds: ReadonlySet manager: ExtensionManager }): Promise { - if (input.distribution === 'swift_bundled') - throw new Error('此分发随应用交付,请在对应 Peer 管理。') + if (input.distribution !== 'python' && input.distribution !== 'module_federation') + throw new Error('此分发的安装方式需由对应 Peer 处理。') const mode = extensionPeerControlMode(input.peer, input.currentPeerId, input.livePeerIds) if (mode === 'current-runtime') { if (input.distribution !== 'module_federation') diff --git a/apps/client-web/src/locales/messages/en.json b/apps/client-web/src/locales/messages/en.json index e289e96c..3b880a66 100644 --- a/apps/client-web/src/locales/messages/en.json +++ b/apps/client-web/src/locales/messages/en.json @@ -256,7 +256,7 @@ "unavailableOnPeer": "Unavailable on this Peer", "publishedDistributions": "Published distributions", "noPublishedDistribution": "No published {distribution} releases.", - "bundledManagedByApp": "This distribution ships with its application. Manage it and consult its documentation in that application, rather than Registry." + "managedByPeer": "Manage this distribution and consult its documentation on the corresponding Peer." }, "job": { "loadFailed": "Unable to load this job.", diff --git a/apps/client-web/src/locales/messages/zh-CN.json b/apps/client-web/src/locales/messages/zh-CN.json index 2a18dcd0..ac53bce2 100644 --- a/apps/client-web/src/locales/messages/zh-CN.json +++ b/apps/client-web/src/locales/messages/zh-CN.json @@ -256,7 +256,7 @@ "unavailableOnPeer": "此 Peer 不可用", "publishedDistributions": "已发布分发", "noPublishedDistribution": "尚无已发布的 {distribution} 版本。", - "bundledManagedByApp": "此分发随应用交付,不通过 Registry 获取版本或文档。请在对应应用中管理。" + "managedByPeer": "请在对应 Peer 管理此分发和查看文档。" }, "job": { "loadFailed": "无法读取此任务。", diff --git a/extensions/capture/src/schema.ts b/extensions/capture/src/schema.ts index 33ceb0f3..c137b2bf 100644 --- a/extensions/capture/src/schema.ts +++ b/extensions/capture/src/schema.ts @@ -2,6 +2,8 @@ import { z } from 'zod' import type { Block, Resolver } from '@inkcre/core' export const captureMetadata = z.object({ + submission_id: z.uuid(), + payload_digest: z.string().regex(/^[0-9a-f]{64}$/), captured_at: z.iso.datetime({ offset: true }), entry: z.enum(['selection', 'paste', 'screenshot', 'manual']), origin: z diff --git a/packages/core/src/extension/model.ts b/packages/core/src/extension/model.ts index 364f5985..ff6f4fa6 100644 --- a/packages/core/src/extension/model.ts +++ b/packages/core/src/extension/model.ts @@ -22,7 +22,7 @@ export const ExtensionVersionSchema = z.string().refine( { error: 'Extension version must be strict SemVer without build metadata.' } ) -export const NativeDistributionSchema = z.enum(['python', 'module_federation', 'swift_bundled']) +export const NativeDistributionSchema = z.string().min(1) export type NativeDistribution = z.infer /** Shared identity and configuration; native versions belong to individual Peers. */ From 4f390abbe84e6965a465d0da996edba9fbf24355 Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Mon, 26 Oct 2026 22:39:10 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix(extensions):=20=E5=AF=B9=E9=BD=90?= =?UTF-8?q?=E4=B8=BB=E7=BA=BF=E5=B7=A5=E5=85=B7=E9=93=BE=E4=B8=8E=20Regist?= =?UTF-8?q?ry=20=E7=89=88=E6=9C=AC=E9=80=89=E6=8B=A9=E8=BE=B9=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增分发沿用工作区浏览器类型与公开 UI 依赖。 - 修正开放分发后的安装器类型检查与合同测试门禁。 --- .changeset/open-peer-distributions.md | 2 +- .../use-extension-version-dialog.ts | 4 +- extensions/capture/package.json | 4 +- extensions/capture/tsconfig.json | 2 +- extensions/local-files/package.json | 4 +- extensions/local-files/tsconfig.json | 2 +- pnpm-lock.yaml | 70 +++++-------------- scripts/tests/local-files-contract.test.ts | 4 +- 8 files changed, 28 insertions(+), 64 deletions(-) diff --git a/.changeset/open-peer-distributions.md b/.changeset/open-peer-distributions.md index ac2822fc..b98a859d 100644 --- a/.changeset/open-peer-distributions.md +++ b/.changeset/open-peer-distributions.md @@ -1,5 +1,5 @@ --- -"@inkcre/core": patch +'@inkcre/core': patch --- 共享 Extension 参与与状态模型保留宿主的分发标识,移除固定语言/打包格式白名单。 diff --git a/apps/client-web/src/components/extension/extensionCard/use-extension-version-dialog.ts b/apps/client-web/src/components/extension/extensionCard/use-extension-version-dialog.ts index 6f84c75f..33dd96bc 100644 --- a/apps/client-web/src/components/extension/extensionCard/use-extension-version-dialog.ts +++ b/apps/client-web/src/components/extension/extensionCard/use-extension-version-dialog.ts @@ -1,4 +1,5 @@ import { computed, onUnmounted, ref, shallowRef } from 'vue' +import { useI18n } from 'vue-i18n' import { configStore, ExtensionParticipationModel, @@ -28,6 +29,7 @@ export function useExtensionVersionDialog(options: { data: ExtensionCardData runtime: ReturnType }) { + const { t } = useI18n() const { data, runtime } = options const selectedPeer = shallowRef(null) const versionOpen = ref(false) @@ -105,7 +107,7 @@ export function useExtensionVersionDialog(options: { : (preferredPublishedRelease(releases)?.version ?? '') if (!releases.length) { // On explicit install, show another published native format without implying this Peer supports it. - const other: NativeDistribution = distribution === 'python' ? 'module_federation' : 'python' + const other = distribution === 'python' ? 'module_federation' : 'python' if (runtime.publishedFormats.value[other] === undefined) await readPublished(other) } } catch (error) { diff --git a/extensions/capture/package.json b/extensions/capture/package.json index 8cc4abe5..e5cca074 100644 --- a/extensions/capture/package.json +++ b/extensions/capture/package.json @@ -15,10 +15,10 @@ }, "devDependencies": { "@inkcre/ext-dev-utils": "workspace:*", - "@inkcre/ui-web": "2.2.1", + "@inkcre/ui-web": "2.3.0", "@module-federation/vite": "^1.1.0", "@vitejs/plugin-vue": "^6.0.1", - "@vue/tsconfig": "^0.7.0", + "@vue/tsconfig": "catalog:", "typescript": "catalog:", "vite": "^7.0.6", "vue-tsc": "catalog:" diff --git a/extensions/capture/tsconfig.json b/extensions/capture/tsconfig.json index dc91eb8d..2069b584 100644 --- a/extensions/capture/tsconfig.json +++ b/extensions/capture/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@vue/tsconfig/tsconfig.dom.json", + "extends": ["@vue/tsconfig/tsconfig.dom.json", "../../tsconfig.browser.json"], "compilerOptions": { "outDir": "./dist/types", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo", diff --git a/extensions/local-files/package.json b/extensions/local-files/package.json index 374eb5b7..8a22016d 100644 --- a/extensions/local-files/package.json +++ b/extensions/local-files/package.json @@ -15,10 +15,10 @@ }, "devDependencies": { "@inkcre/ext-dev-utils": "workspace:*", - "@inkcre/ui-web": "2.2.1", + "@inkcre/ui-web": "2.3.0", "@module-federation/vite": "^1.1.0", "@vitejs/plugin-vue": "^6.0.1", - "@vue/tsconfig": "^0.7.0", + "@vue/tsconfig": "catalog:", "typescript": "catalog:", "vite": "^7.0.6", "vue-tsc": "catalog:" diff --git a/extensions/local-files/tsconfig.json b/extensions/local-files/tsconfig.json index dc91eb8d..2069b584 100644 --- a/extensions/local-files/tsconfig.json +++ b/extensions/local-files/tsconfig.json @@ -1,5 +1,5 @@ { - "extends": "@vue/tsconfig/tsconfig.dom.json", + "extends": ["@vue/tsconfig/tsconfig.dom.json", "../../tsconfig.browser.json"], "compilerOptions": { "outDir": "./dist/types", "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.tsbuildinfo", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 51ab78d3..14472883 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -382,8 +382,8 @@ importers: specifier: workspace:* version: link:../../packages/ext-dev-utils '@inkcre/ui-web': - specifier: 2.2.1 - version: 2.2.1(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + specifier: https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz + version: https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) '@module-federation/vite': specifier: ^1.1.0 version: 1.7.1(rollup@4.59.0) @@ -391,8 +391,8 @@ importers: specifier: ^6.0.1 version: 6.0.3(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) '@vue/tsconfig': - specifier: ^0.7.0 - version: 0.7.0(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + specifier: 'catalog:' + version: 0.9.1(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) typescript: specifier: npm:typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 version: typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 @@ -459,16 +459,16 @@ importers: version: link:../../packages/core '@vueuse/core': specifier: ^14.0.0 - version: 14.4.0(vue@3.5.40(typescript@5.9.3)) + version: 14.4.0(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) pinia: specifier: ^3.0.0 - version: 3.0.3(typescript@5.9.3)(vue@3.5.40(typescript@5.9.3)) + version: 3.0.3(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) vue: specifier: ^3.5.25 - version: 3.5.40(typescript@5.9.3) + version: 3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2) vue-router: specifier: ^4.5.0 - version: 4.5.1(vue@3.5.40(typescript@5.9.3)) + version: 4.5.1(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) zod: specifier: ^4.1.0 version: 4.4.3 @@ -477,26 +477,26 @@ importers: specifier: workspace:* version: link:../../packages/ext-dev-utils '@inkcre/ui-web': - specifier: 2.2.1 - version: 2.2.1(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript@5.9.3)) + specifier: https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz + version: https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) '@module-federation/vite': specifier: ^1.1.0 version: 1.7.1(rollup@4.59.0) '@vitejs/plugin-vue': specifier: ^6.0.1 - version: 6.0.3(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0))(vue@3.5.40(typescript@5.9.3)) + version: 6.0.3(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) '@vue/tsconfig': - specifier: ^0.7.0 - version: 0.7.0(typescript@5.9.3)(vue@3.5.40(typescript@5.9.3)) - typescript: specifier: 'catalog:' - version: 5.9.3 + version: 0.9.1(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) + typescript: + specifier: npm:typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 + version: typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 vite: specifier: ^7.0.6 version: 7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0) vue-tsc: specifier: 'catalog:' - version: 3.3.8(typescript@5.9.3) + version: 3.3.8(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2) extensions/mail: dependencies: @@ -1626,12 +1626,6 @@ packages: '@inkcre/core': '>=0.4.0 <0.5.0' '@module-federation/runtime': '>=0.21.4 <0.23.0' - '@inkcre/ui-web@2.2.1': - resolution: {integrity: sha512-rsljLVVzpzCRhywF0poVBFl21H48JAQwENwci+efL5/YOLjav9Uv7yh8oMN4eqmOiQw9Hgt4Tl6Epg2Rzb/g+w==, tarball: https://npm.pkg.github.com/download/@inkcre/ui-web/2.2.1/5b22fbb31e4bf02e4f742bb0f9353cacfb4b8fd9} - peerDependencies: - unocss: ^66.5.0 - vue: ^3.5.25 - '@inkcre/ui-web@https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz': resolution: {integrity: sha512-fXfAro12+zzyhtjiZqfQBvyMJKVnclrm/qvHst70dnRFZekYIkE+rSfabEJ//avAvvJyVZL7kK6XewXK4PgdJg==, tarball: https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz} version: 2.3.0 @@ -3194,17 +3188,6 @@ packages: '@vue/shared@3.5.40': resolution: {integrity: sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg==} - '@vue/tsconfig@0.7.0': - resolution: {integrity: sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==} - peerDependencies: - typescript: 5.x - vue: ^3.4.0 - peerDependenciesMeta: - typescript: - optional: true - vue: - optional: true - '@vue/tsconfig@0.9.1': resolution: {integrity: sha512-buvjm+9NzLCJL29KY1j1991YYJ5e6275OiK+G4jtmfIb+z4POywbdm0wXusT9adVWqe0xqg70TbI7+mRx4uU9w==} peerDependencies: @@ -7135,22 +7118,6 @@ snapshots: semver: 7.8.5 zod: 4.1.12 - '@inkcre/ui-web@2.2.1(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2))': - dependencies: - '@codemirror/autocomplete': 6.20.3 - '@codemirror/commands': 6.11.1 - '@codemirror/lang-json': 6.0.2 - '@codemirror/lint': 6.9.7 - '@codemirror/state': 6.7.5 - '@codemirror/view': 6.43.12 - '@vue/shared': 3.5.25 - '@vueuse/core': 14.4.0(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)) - dayjs: 1.11.23 - unocss: 66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)) - vscode-json-languageservice: 5.6.4 - vscode-languageserver-textdocument: 1.0.14 - vue: 3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2) - '@inkcre/ui-web@https://github.com/InKCre/ui/releases/download/%40inkcre%2Fui-web%402.3.0/inkcre-ui-web-2.3.0.tgz(unocss@66.7.5(vite@7.3.6(@types/node@22.17.2)(jiti@2.7.0)(lightningcss@1.33.0)(sass-embedded@1.90.0)(sass@1.101.6)(yaml@2.9.0)))(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2))': dependencies: '@codemirror/autocomplete': 6.20.3 @@ -8505,11 +8472,6 @@ snapshots: '@vue/shared@3.5.40': {} - '@vue/tsconfig@0.7.0(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2))': - optionalDependencies: - typescript: typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 - vue: 3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2) - '@vue/tsconfig@0.9.1(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2)(vue@3.5.40(typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2))': optionalDependencies: typescript: typescript-native-bridge@6.0.3-bridge.19.tsgo.7.0.2 diff --git a/scripts/tests/local-files-contract.test.ts b/scripts/tests/local-files-contract.test.ts index e6f6c659..c1f74481 100644 --- a/scripts/tests/local-files-contract.test.ts +++ b/scripts/tests/local-files-contract.test.ts @@ -2,7 +2,7 @@ import assert from 'node:assert/strict' import test from 'node:test' import { fileMetadata, peerFileLocation } from '../../extensions/local-files/src/schema.ts' -test('Peer file location preserves Unicode and reserved path characters', () => { +void test('Peer file location preserves Unicode and reserved path characters', () => { const result = peerFileLocation( 'inkcre-peer-file://a143b185-f43d-47d1-8ef7-1d3857284b01/Users/a/%E4%B8%AD%E6%96%87%20%23%3F.md' ) @@ -16,7 +16,7 @@ test('Peer file location preserves Unicode and reserved path characters', () => } }) -test('Missing originals and failed extraction retain a distinct successful revision', () => { +void test('Missing originals and failed extraction retain a distinct successful revision', () => { const metadata = fileMetadata.parse({ location: 'inkcre-peer-file://a143b185-f43d-47d1-8ef7-1d3857284b01/Users/a/report.docx', format: 'docx', From fea292c7fe935030fdd995095d2cf0aa086ee3be Mon Sep 17 00:00:00 2001 From: Lan_zhijiang Date: Mon, 26 Oct 2026 22:39:10 +0800 Subject: [PATCH 8/8] =?UTF-8?q?docs(shared):=20=E5=AF=B9=E9=BD=90=E5=B7=B2?= =?UTF-8?q?=E5=90=88=E5=B9=B6=E7=9A=84=E7=8B=AC=E7=AB=8B=20Peer=20?= =?UTF-8?q?=E4=B8=8E=E6=96=87=E4=BB=B6=E6=94=B6=E5=85=A5=E5=90=88=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/_shared | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/_shared b/docs/_shared index 831e8358..311c69bd 160000 --- a/docs/_shared +++ b/docs/_shared @@ -1 +1 @@ -Subproject commit 831e8358a87894ecf29f25e18bff2c2ef3ce42bb +Subproject commit 311c69bdada4fb291cc7fae0ded5caefa4fdc867