refactor(libs): 抽取 @yggdrasil/shared 消除跨 IIFE 库的类型/常量重复
新增 libs/shared 内部包,作为跨 IIFE 库的单一真相源: - ThemeName 类型(原 codemirror/themes.ts + xterm/themes.ts 各一份) - THEME_CHANGE_EVENT 常量(原 yggdrasil-core 导出 + codemirror/xterm 各硬编码同名 string literal,靠注释维系一致) - prefersReducedMotion 函数(原 lightbox + yggdrasil-core 各一份) codemirror-editor / xterm-terminal / lightbox / yggdrasil-core 四个库 改为 workspace 依赖 @yggdrasil/shared,Vite 构建时 inline 进各自 IIFE, 不破坏 IIFE 隔离。消除「改一处忘改另一处导致静默失效」的风险。 附:image_reader_limits 已在上一提交共享;本提交是 libs 侧的对称改动。
This commit is contained in:
parent
6837b4cfa2
commit
9bc8b5c2ee
@ -23,6 +23,7 @@
|
|||||||
"@codemirror/state": "^6.7.1",
|
"@codemirror/state": "^6.7.1",
|
||||||
"@codemirror/view": "^6.43.6",
|
"@codemirror/view": "^6.43.6",
|
||||||
"@replit/codemirror-vim": "^6.3.0",
|
"@replit/codemirror-vim": "^6.3.0",
|
||||||
|
"@yggdrasil/shared": "workspace:*",
|
||||||
"codemirror": "^6.0.2"
|
"codemirror": "^6.0.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
|
import { THEME_CHANGE_EVENT } from '@yggdrasil/shared';
|
||||||
import { CodeMirrorInstance, EditorOptions } from './editor';
|
import { CodeMirrorInstance, EditorOptions } from './editor';
|
||||||
import type { ThemeName } from './themes';
|
import type { ThemeName } from './themes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主题切换事件名——与 yggdrasil-core 的 THEME_CHANGE_EVENT 保持一致。
|
* 主题切换事件由 @yggdrasil/shared 统一定义。
|
||||||
*
|
*
|
||||||
* 本包是独立 IIFE,不 import yggdrasil-core,故用同名 string literal 订阅。
|
|
||||||
* yggdrasil-core 在 VT 回调内(NEW 快照捕获前)同步 dispatch 此事件,
|
* yggdrasil-core 在 VT 回调内(NEW 快照捕获前)同步 dispatch 此事件,
|
||||||
* 让 CodeMirror 同步 reconfigure 主题——否则圆形展开扫过编辑器区域时
|
* 让 CodeMirror 同步 reconfigure 主题——否则圆形展开扫过编辑器区域时
|
||||||
* OLD/NEW 快照同色(背景由 catppuccin Extension 注入,不随 .dark 翻转),
|
* OLD/NEW 快照同色(背景由 catppuccin Extension 注入,不随 .dark 翻转),
|
||||||
* 看不到变化,动画结束后才瞬切。
|
* 看不到变化,动画结束后才瞬切。
|
||||||
*/
|
*/
|
||||||
const THEME_CHANGE_EVENT = 'yggdrasil:theme-change';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模块入口:暴露对象字面量 { create } 作为默认导出。
|
* 模块入口:暴露对象字面量 { create } 作为默认导出。
|
||||||
|
|||||||
@ -3,8 +3,9 @@
|
|||||||
import { catppuccinLatte, catppuccinMocha } from '@catppuccin/codemirror';
|
import { catppuccinLatte, catppuccinMocha } from '@catppuccin/codemirror';
|
||||||
import type { Extension } from '@codemirror/state';
|
import type { Extension } from '@codemirror/state';
|
||||||
import { EditorView } from '@codemirror/view';
|
import { EditorView } from '@codemirror/view';
|
||||||
|
import type { ThemeName } from '@yggdrasil/shared';
|
||||||
|
|
||||||
export type ThemeName = 'light' | 'dark';
|
export type { ThemeName };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 覆盖 CodeMirror core 内置 base theme 的两处问题:
|
* 覆盖 CodeMirror core 内置 base theme 的两处问题:
|
||||||
|
|||||||
@ -9,5 +9,8 @@
|
|||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest"
|
"test:watch": "vitest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@yggdrasil/shared": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { prefersReducedMotion } from '@yggdrasil/shared';
|
||||||
import { fitCentered, originalUrl, type Rect, transformFor } from './geometry';
|
import { fitCentered, originalUrl, type Rect, transformFor } from './geometry';
|
||||||
import './style.css';
|
import './style.css';
|
||||||
|
|
||||||
@ -33,9 +34,7 @@ declare global {
|
|||||||
|
|
||||||
// ============ 工具函数 ============
|
// ============ 工具函数 ============
|
||||||
|
|
||||||
function prefersReducedMotion(): boolean {
|
// prefersReducedMotion 由 @yggdrasil/shared 提供(lightbox.test.ts 会 mock matchMedia)。
|
||||||
return !!window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 读取元素当前在视口里的 rect(用于飞行起点/终点)。
|
// 读取元素当前在视口里的 rect(用于飞行起点/终点)。
|
||||||
// 统一映射成 {x,y,w,h}:getBoundingClientRect 返回的 DOMRect 用
|
// 统一映射成 {x,y,w,h}:getBoundingClientRect 返回的 DOMRect 用
|
||||||
|
|||||||
20
libs/pnpm-lock.yaml
generated
20
libs/pnpm-lock.yaml
generated
@ -62,11 +62,20 @@ importers:
|
|||||||
'@replit/codemirror-vim':
|
'@replit/codemirror-vim':
|
||||||
specifier: ^6.3.0
|
specifier: ^6.3.0
|
||||||
version: 6.3.0(@codemirror/commands@6.10.4)(@codemirror/language@6.12.4)(@codemirror/search@6.7.1)(@codemirror/state@6.7.1)(@codemirror/view@6.43.6)
|
version: 6.3.0(@codemirror/commands@6.10.4)(@codemirror/language@6.12.4)(@codemirror/search@6.7.1)(@codemirror/state@6.7.1)(@codemirror/view@6.43.6)
|
||||||
|
'@yggdrasil/shared':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../shared
|
||||||
codemirror:
|
codemirror:
|
||||||
specifier: ^6.0.2
|
specifier: ^6.0.2
|
||||||
version: 6.0.2
|
version: 6.0.2
|
||||||
|
|
||||||
lightbox: {}
|
lightbox:
|
||||||
|
dependencies:
|
||||||
|
'@yggdrasil/shared':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../shared
|
||||||
|
|
||||||
|
shared: {}
|
||||||
|
|
||||||
tiptap-editor:
|
tiptap-editor:
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -115,8 +124,15 @@ importers:
|
|||||||
'@xterm/xterm':
|
'@xterm/xterm':
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.0
|
version: 6.0.0
|
||||||
|
'@yggdrasil/shared':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../shared
|
||||||
|
|
||||||
yggdrasil-core: {}
|
yggdrasil-core:
|
||||||
|
dependencies:
|
||||||
|
'@yggdrasil/shared':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../shared
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
|||||||
14
libs/shared/package.json
Normal file
14
libs/shared/package.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "@yggdrasil/shared",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"main": "src/index.ts",
|
||||||
|
"types": "src/index.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --noEmit",
|
||||||
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "vitest run",
|
||||||
|
"test:watch": "vitest"
|
||||||
|
}
|
||||||
|
}
|
||||||
19
libs/shared/src/index.test.ts
Normal file
19
libs/shared/src/index.test.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { describe, expect, it } from 'vitest';
|
||||||
|
import { prefersReducedMotion, THEME_CHANGE_EVENT, type ThemeName } from './index';
|
||||||
|
|
||||||
|
describe('@yggdrasil/shared', () => {
|
||||||
|
it('THEME_CHANGE_EVENT 是固定字符串', () => {
|
||||||
|
expect(THEME_CHANGE_EVENT).toBe('yggdrasil:theme-change');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prefersReducedMotion 在无 matchMedia 时返回 false', () => {
|
||||||
|
// happy-dom 提供了 matchMedia,这里只验证函数可调用且返回 boolean
|
||||||
|
const result = prefersReducedMotion();
|
||||||
|
expect(typeof result).toBe('boolean');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ThemeName 类型仅接受 light/dark(编译期约束)', () => {
|
||||||
|
const t: ThemeName = 'light';
|
||||||
|
expect(['light', 'dark']).toContain(t);
|
||||||
|
});
|
||||||
|
});
|
||||||
27
libs/shared/src/index.ts
Normal file
27
libs/shared/src/index.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/**
|
||||||
|
* 跨 IIFE 库共享的类型、常量与工具函数。
|
||||||
|
*
|
||||||
|
* 这些库各自打包成独立 IIFE(不能 import 彼此),但它们的主题切换逻辑
|
||||||
|
* 需要约定相同的事件名与类型定义。本包作为单一真相源,由各库以 workspace
|
||||||
|
* dependency 引入,Vite 构建时 inline 进各自的 IIFE bundle。
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 主题名称:亮色 / 暗色。 */
|
||||||
|
export type ThemeName = 'light' | 'dark';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主题切换事件名。
|
||||||
|
*
|
||||||
|
* yggdrasil-core 在切换主题时 dispatch 此事件;codemirror-editor / xterm-terminal
|
||||||
|
* 监听它以热切换编辑器主题。所有库必须用同一个字符串字面量。
|
||||||
|
*/
|
||||||
|
export const THEME_CHANGE_EVENT = 'yggdrasil:theme-change';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检测用户是否在系统层面启用了「减少动态效果」(prefers-reduced-motion)。
|
||||||
|
*
|
||||||
|
* 用于决定是否跳过 View Transitions / 动画,直接切换。
|
||||||
|
*/
|
||||||
|
export function prefersReducedMotion(): boolean {
|
||||||
|
return !!window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
||||||
|
}
|
||||||
4
libs/shared/tsconfig.json
Normal file
4
libs/shared/tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "../tsconfig.base.json",
|
||||||
|
"include": ["src"]
|
||||||
|
}
|
||||||
9
libs/shared/vitest.config.ts
Normal file
9
libs/shared/vitest.config.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
// prefersReducedMotion 读 window.matchMedia,需要 happy-dom。
|
||||||
|
environment: 'happy-dom',
|
||||||
|
include: ['src/**/*.test.ts'],
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -12,6 +12,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xterm/addon-fit": "^0.11.0",
|
"@xterm/addon-fit": "^0.11.0",
|
||||||
"@xterm/xterm": "^6.0.0"
|
"@xterm/xterm": "^6.0.0",
|
||||||
|
"@yggdrasil/shared": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,15 @@
|
|||||||
|
import { THEME_CHANGE_EVENT } from '@yggdrasil/shared';
|
||||||
import { TerminalInstance, XtermOptions } from './terminal';
|
import { TerminalInstance, XtermOptions } from './terminal';
|
||||||
import type { ThemeName } from './themes';
|
import type { ThemeName } from './themes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主题切换事件名——与 yggdrasil-core 的 THEME_CHANGE_EVENT 保持一致。
|
* 主题切换事件由 @yggdrasil/shared 统一定义。
|
||||||
*
|
*
|
||||||
* 本包是独立 IIFE,不 import yggdrasil-core,故用同名 string literal 订阅。
|
|
||||||
* yggdrasil-core 在 VT 回调内(NEW 快照捕获前)同步 dispatch 此事件,
|
* yggdrasil-core 在 VT 回调内(NEW 快照捕获前)同步 dispatch 此事件,
|
||||||
* 让 xterm 同步 setTheme——否则圆形展开扫过终端区域时 OLD/NEW 快照同色
|
* 让 xterm 同步 setTheme——否则圆形展开扫过终端区域时 OLD/NEW 快照同色
|
||||||
* (背景由 .xterm-scrollable-element 的 inline background-color 注入,不随
|
* (背景由 .xterm-scrollable-element 的 inline background-color 注入,不随
|
||||||
* .dark 翻转),看不到变化,动画结束后才瞬切。
|
* .dark 翻转),看不到变化,动画结束后才瞬切。
|
||||||
*/
|
*/
|
||||||
const THEME_CHANGE_EVENT = 'yggdrasil:theme-change';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模块入口:暴露对象字面量 { create } 作为默认导出。
|
* 模块入口:暴露对象字面量 { create } 作为默认导出。
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
// 主题名:与 codemirror-editor / 项目 themes/ 下 Catppuccin Latte/Mocha 对齐。
|
// 主题名由 @yggdrasil/shared 统一定义,re-export 保持本模块 API 不变。
|
||||||
export type ThemeName = 'light' | 'dark';
|
export type { ThemeName } from '@yggdrasil/shared';
|
||||||
|
|
||||||
// Catppuccin Latte(浅色)终端配色,与 highlight.css 浅色主题视觉一致。
|
// Catppuccin Latte(浅色)终端配色,与 highlight.css 浅色主题视觉一致。
|
||||||
// background 用项目 --color-paper-code-block 的色值(Catppuccin Latte Surface0),
|
// background 用项目 --color-paper-code-block 的色值(Catppuccin Latte Surface0),
|
||||||
|
|||||||
@ -9,5 +9,8 @@
|
|||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
"test": "vitest run",
|
"test": "vitest run",
|
||||||
"test:watch": "vitest"
|
"test:watch": "vitest"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@yggdrasil/shared": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,8 +10,10 @@
|
|||||||
* API 优先级 bug,是目前最稳定的 VT 主题切换方案。
|
* API 优先级 bug,是目前最稳定的 VT 主题切换方案。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { prefersReducedMotion, THEME_CHANGE_EVENT } from '@yggdrasil/shared';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 主题切换自定义事件名。
|
* 主题切换自定义事件。
|
||||||
*
|
*
|
||||||
* 在 VT 回调内(NEW 快照捕获前)同步 dispatch,通知 CodeMirror / xterm 等
|
* 在 VT 回调内(NEW 快照捕获前)同步 dispatch,通知 CodeMirror / xterm 等
|
||||||
* 命令式换肤的组件同步调 setTheme——它们的背景色不随 .dark class 翻转,
|
* 命令式换肤的组件同步调 setTheme——它们的背景色不随 .dark class 翻转,
|
||||||
@ -19,14 +21,10 @@
|
|||||||
*
|
*
|
||||||
* 事件 detail: `{ isDark: boolean }`。
|
* 事件 detail: `{ isDark: boolean }`。
|
||||||
*
|
*
|
||||||
* 各编辑器包(codemirror-editor / xterm-terminal)是独立 IIFE,不 import 本包,
|
* 事件名与 prefersReducedMotion 由 @yggdrasil/shared 统一定义,各 IIFE 库
|
||||||
* 故各自用同名 string literal 订阅;本常量仅用于本包内部 + 测试断言一致性。
|
* (codemirror-editor / xterm-terminal / lightbox)共享同一真相源。
|
||||||
*/
|
*/
|
||||||
export const THEME_CHANGE_EVENT = 'yggdrasil:theme-change';
|
export { THEME_CHANGE_EVENT };
|
||||||
|
|
||||||
function prefersReducedMotion(): boolean {
|
|
||||||
return !!window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
function maxCornerDistance(x: number, y: number): number {
|
function maxCornerDistance(x: number, y: number): number {
|
||||||
const w = window.innerWidth;
|
const w = window.innerWidth;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user