diff --git a/.changeset/capture-reader.md b/.changeset/capture-reader.md new file mode 100644 index 00000000..113115ef --- /dev/null +++ b/.changeset/capture-reader.md @@ -0,0 +1,9 @@ +--- +'@inkcre/ext-capture': minor +'@inkcre/core': patch +'@inkcre/client-web': patch +--- + +新增随手收集分发,从共享信息库读取有序原文、原始图片、真实来源与用户附注。展示不依赖原 Mac 或 Python 执行 Peer 在线。 + +Core 与 Web 可解释随签名应用交付的 swift_bundled 参与身份;版本管理提示转到对应应用,不调用不支持该格式的 Registry 接口。 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/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..c8866dd6 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 === 'swift_bundled') { + docsError.value = t('extension.bundledManagedByApp') + 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..e9b4d0c4 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)' + : 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..97a19e5e 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,3 +1,4 @@ +import { useI18n } from 'vue-i18n' import { computed, onUnmounted, ref, shallowRef } from 'vue' import { configStore, @@ -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) @@ -68,7 +70,14 @@ export function useExtensionVersionDialog(options: { versionLoading.value = true versionOptions.value = [] versionError.value = '' - const readPublished = async (format: NativeDistribution): Promise => { + if (distribution === 'swift_bundled') { + versionError.value = t('extension.bundledManagedByApp') + 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 ?? []) @@ -98,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/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 5bf893d4..3777e02c 100644 --- a/apps/client-web/src/locales/messages/en.json +++ b/apps/client-web/src/locales/messages/en.json @@ -251,7 +251,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 ed6d5508..ef83a7e1 100644 --- a/apps/client-web/src/locales/messages/zh-CN.json +++ b/apps/client-web/src/locales/messages/zh-CN.json @@ -251,7 +251,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 new file mode 100644 index 00000000..414ec300 --- /dev/null +++ b/extensions/capture/README.md @@ -0,0 +1,11 @@ +# 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` 只移除本分发拥有的类。Swift、Python 与 Web 分发按各自能力对等参与同一 Extension。 + +共享图与提交语义由 [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,检查原文、图片字节、顺序和附注。 + +Mac 自行通过共享数据库原子写入,不依赖 Python Peer。此 Web 分发只负责读取与呈现,未启用它不影响采集写入。 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..5d559a7f --- /dev/null +++ b/extensions/capture/package.json @@ -0,0 +1,46 @@ +{ + "name": "@inkcre/ext-capture", + "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": "catalog:", + "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..2069b584 --- /dev/null +++ b/extensions/capture/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": ["@vue/tsconfig/tsconfig.dom.json", "../../tsconfig.browser.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..b26ef9db --- /dev/null +++ b/extensions/capture/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: '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', + 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/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. */ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 207ec806..0b5a8498 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -355,6 +355,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: '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 + 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': @@ -1532,6 +1578,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@2.3.0': resolution: {integrity: sha512-fXfAro12+zzyhtjiZqfQBvyMJKVnclrm/qvHst70dnRFZekYIkE+rSfabEJ//avAvvJyVZL7kK6XewXK4PgdJg==, tarball: https://npm.pkg.github.com/download/@inkcre/ui-web/2.3.0/2c81dee78ac6797018f0e62a2a988747cd3c7a3e} peerDependencies: @@ -7016,6 +7068,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@2.3.0(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 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',