clickgo 6.4.2 → 6.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app/demo.cga +0 -0
- package/dist/app/task.cga +0 -0
- package/dist/control/arteditor.cgc +0 -0
- package/dist/control/captcha.cgc +0 -0
- package/dist/control/common.cgc +0 -0
- package/dist/control/desc.cgc +0 -0
- package/dist/control/drawer.cgc +0 -0
- package/dist/control/form.cgc +0 -0
- package/dist/control/iconview.cgc +0 -0
- package/dist/control/jodit.cgc +0 -0
- package/dist/control/map.cgc +0 -0
- package/dist/control/monaco.cgc +0 -0
- package/dist/control/mpegts.cgc +0 -0
- package/dist/control/nav.cgc +0 -0
- package/dist/control/page.cgc +0 -0
- package/dist/control/property.cgc +0 -0
- package/dist/control/swiper.cgc +0 -0
- package/dist/control/table.cgc +0 -0
- package/dist/control/task.cgc +0 -0
- package/dist/control/tuieditor.cgc +0 -0
- package/dist/control/xterm.cgc +0 -0
- package/dist/global.css +1 -1
- package/dist/index.js +1 -1
- package/dist/lib/control.d.ts +4 -0
- package/dist/lib/dom.d.ts +30 -0
- package/dist/lib/form.d.ts +9 -0
- package/dist/lib/tool.d.ts +12 -0
- package/dist/theme/admin.cgt +0 -0
- package/dist/theme/cyber.cgt +0 -0
- package/dist/theme/dark.cgt +0 -0
- package/dist/theme/light.cgt +0 -0
- package/dist/theme/modern-light.cgt +0 -0
- package/dist/theme/modern.cgt +0 -0
- package/doc/clickgo-rag.md +1347 -946
- package/package.json +5 -2
- package/test/locale-integrity.mjs +189 -0
- package/test/rtl-archive.mjs +416 -0
- package/test/rtl-direction.mjs +193 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
// Run: node --experimental-vm-modules test/rtl-direction.mjs
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { SourceTextModule, SyntheticModule } from 'node:vm';
|
|
5
|
+
import { JSDOM } from 'jsdom';
|
|
6
|
+
import ts from 'typescript';
|
|
7
|
+
|
|
8
|
+
const dom = new JSDOM('<!doctype html><html><head></head><body></body></html>');
|
|
9
|
+
for (const name of ['window', 'document', 'navigator', 'Element', 'SVGElement', 'Node', 'HTMLElement', 'PointerEvent']) {
|
|
10
|
+
Object.defineProperty(globalThis, name, {
|
|
11
|
+
'configurable': true,
|
|
12
|
+
'value': dom.window[name],
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
globalThis.getComputedStyle = dom.window.getComputedStyle.bind(dom.window);
|
|
16
|
+
globalThis.ResizeObserver = class {
|
|
17
|
+
observe() {}
|
|
18
|
+
unobserve() {}
|
|
19
|
+
disconnect() {}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/** --- 当前模拟任务的 locale 状态 --- */
|
|
23
|
+
const tasks = new Map([
|
|
24
|
+
['arabic', { 'locale': { 'lang': 'ar' } }],
|
|
25
|
+
['english', { 'locale': { 'lang': 'en' } }],
|
|
26
|
+
['inherit', { 'locale': { 'lang': '' } }],
|
|
27
|
+
]);
|
|
28
|
+
/** --- 系统 locale,可在测试中模拟运行时切换 --- */
|
|
29
|
+
const config = { 'locale': 'en' };
|
|
30
|
+
|
|
31
|
+
const toolSource = await readFile(new URL('../dist/lib/tool.ts', import.meta.url), 'utf8');
|
|
32
|
+
const toolModule = new SourceTextModule(ts.transpileModule(toolSource, {
|
|
33
|
+
'compilerOptions': {
|
|
34
|
+
'target': ts.ScriptTarget.ES2022,
|
|
35
|
+
'module': ts.ModuleKind.ESNext,
|
|
36
|
+
},
|
|
37
|
+
}).outputText);
|
|
38
|
+
await toolModule.link(() => {
|
|
39
|
+
throw new Error('tool.ts must not import runtime modules');
|
|
40
|
+
});
|
|
41
|
+
await toolModule.evaluate();
|
|
42
|
+
|
|
43
|
+
const clickgo = new SyntheticModule(['modules'], function() {
|
|
44
|
+
this.setExport('modules', {
|
|
45
|
+
'pointer': {
|
|
46
|
+
'move': () => {},
|
|
47
|
+
},
|
|
48
|
+
'vue': {
|
|
49
|
+
'createApp': () => ({
|
|
50
|
+
'mount': () => {},
|
|
51
|
+
}),
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
const core = new SyntheticModule(['config'], function() {
|
|
56
|
+
this.setExport('config', config);
|
|
57
|
+
});
|
|
58
|
+
const task = new SyntheticModule(['getOrigin'], function() {
|
|
59
|
+
this.setExport('getOrigin', (taskId) => tasks.get(taskId) ?? null);
|
|
60
|
+
});
|
|
61
|
+
const tool = new SyntheticModule(['lang', 'logicalOr'], function() {
|
|
62
|
+
this.setExport('lang', toolModule.namespace.lang);
|
|
63
|
+
this.setExport('logicalOr', toolModule.namespace.logicalOr);
|
|
64
|
+
});
|
|
65
|
+
const empty = new SyntheticModule([], function() {});
|
|
66
|
+
|
|
67
|
+
const formSource = await readFile(new URL('../dist/lib/form.ts', import.meta.url), 'utf8');
|
|
68
|
+
const formModule = new SourceTextModule(ts.transpileModule(formSource, {
|
|
69
|
+
'compilerOptions': {
|
|
70
|
+
'target': ts.ScriptTarget.ES2022,
|
|
71
|
+
'module': ts.ModuleKind.ESNext,
|
|
72
|
+
},
|
|
73
|
+
}).outputText);
|
|
74
|
+
await formModule.link((specifier) => {
|
|
75
|
+
if (specifier === '../clickgo') {
|
|
76
|
+
return clickgo;
|
|
77
|
+
}
|
|
78
|
+
if (specifier === './core') {
|
|
79
|
+
return core;
|
|
80
|
+
}
|
|
81
|
+
if (specifier === './task') {
|
|
82
|
+
return task;
|
|
83
|
+
}
|
|
84
|
+
if (specifier === './tool') {
|
|
85
|
+
return tool;
|
|
86
|
+
}
|
|
87
|
+
return empty;
|
|
88
|
+
});
|
|
89
|
+
await formModule.evaluate();
|
|
90
|
+
|
|
91
|
+
const domSource = await readFile(new URL('../dist/lib/dom.ts', import.meta.url), 'utf8');
|
|
92
|
+
const domModule = new SourceTextModule(ts.transpileModule(domSource, {
|
|
93
|
+
'compilerOptions': {
|
|
94
|
+
'target': ts.ScriptTarget.ES2022,
|
|
95
|
+
'module': ts.ModuleKind.ESNext,
|
|
96
|
+
},
|
|
97
|
+
}).outputText);
|
|
98
|
+
await domModule.link((specifier) => {
|
|
99
|
+
if (specifier === '../clickgo') {
|
|
100
|
+
return clickgo;
|
|
101
|
+
}
|
|
102
|
+
if (specifier === './form') {
|
|
103
|
+
return formModule;
|
|
104
|
+
}
|
|
105
|
+
if (specifier === './core') {
|
|
106
|
+
return core;
|
|
107
|
+
}
|
|
108
|
+
if (specifier === './task') {
|
|
109
|
+
return task;
|
|
110
|
+
}
|
|
111
|
+
return empty;
|
|
112
|
+
});
|
|
113
|
+
await domModule.evaluate();
|
|
114
|
+
|
|
115
|
+
const { isRtl } = domModule.namespace;
|
|
116
|
+
const rtlElement = document.createElement('div');
|
|
117
|
+
rtlElement.style.direction = 'rtl';
|
|
118
|
+
document.body.append(rtlElement);
|
|
119
|
+
assert.equal(isRtl(rtlElement), true, 'an RTL element must resolve to RTL');
|
|
120
|
+
assert.equal(isRtl(document.createComment('pending render')), false, 'a Vue placeholder node must safely resolve to LTR');
|
|
121
|
+
assert.equal(isRtl(null), false, 'a missing element must safely resolve to LTR');
|
|
122
|
+
|
|
123
|
+
const { elements, refreshLocaleDirection } = formModule.namespace;
|
|
124
|
+
elements.init();
|
|
125
|
+
assert.equal(document.querySelector('#cg-wrap'), elements.wrap, 'system root must initialize as #cg-wrap');
|
|
126
|
+
assert.equal(elements.wrap.lang, 'en', 'system root must receive the global language tag');
|
|
127
|
+
assert.equal(elements.wrap.dir, 'ltr', 'system root must receive the global direction');
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* --- 创建同一任务的 Form 与 pop 容器 ---
|
|
131
|
+
* @param taskId ClickGo task id
|
|
132
|
+
* @param formId ClickGo form id
|
|
133
|
+
* @returns 两个语言容器
|
|
134
|
+
*/
|
|
135
|
+
function appendTaskWrap(taskId, formId) {
|
|
136
|
+
const formWrap = document.createElement('div');
|
|
137
|
+
formWrap.className = 'cg-form-wrap';
|
|
138
|
+
formWrap.dataset.taskId = taskId;
|
|
139
|
+
formWrap.dataset.formId = formId;
|
|
140
|
+
const popWrap = document.createElement('div');
|
|
141
|
+
popWrap.dataset.taskId = taskId;
|
|
142
|
+
popWrap.dataset.formId = formId;
|
|
143
|
+
elements.list.append(formWrap);
|
|
144
|
+
elements.popList.append(popWrap);
|
|
145
|
+
return { formWrap, popWrap };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const arabic = appendTaskWrap('arabic', 'form-ar');
|
|
149
|
+
const english = appendTaskWrap('english', 'form-en');
|
|
150
|
+
const inherited = appendTaskWrap('inherit', 'form-inherit');
|
|
151
|
+
refreshLocaleDirection();
|
|
152
|
+
for (const wrap of [arabic.formWrap, arabic.popWrap]) {
|
|
153
|
+
assert.equal(wrap.lang, 'ar', 'Arabic Form and pop wrappers must receive lang=ar');
|
|
154
|
+
assert.equal(wrap.dir, 'rtl', 'Arabic Form and pop wrappers must receive RTL');
|
|
155
|
+
}
|
|
156
|
+
for (const wrap of [english.formWrap, english.popWrap, inherited.formWrap, inherited.popWrap]) {
|
|
157
|
+
assert.equal(wrap.lang, 'en', 'English and inherited Form/pop wrappers must receive lang=en');
|
|
158
|
+
assert.equal(wrap.dir, 'ltr', 'English and inherited Form/pop wrappers must receive LTR');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// A task-only refresh must update that task's Form and pop wrappers without disturbing a concurrent task.
|
|
162
|
+
tasks.get('arabic').locale.lang = 'en';
|
|
163
|
+
refreshLocaleDirection('arabic');
|
|
164
|
+
for (const wrap of [arabic.formWrap, arabic.popWrap]) {
|
|
165
|
+
assert.equal(wrap.lang, 'en', 'task locale switch ar -> en must update both wrapper types');
|
|
166
|
+
assert.equal(wrap.dir, 'ltr', 'task locale switch ar -> en must update both wrapper types');
|
|
167
|
+
}
|
|
168
|
+
assert.equal(english.formWrap.dir, 'ltr', 'task-only refresh must preserve another task direction');
|
|
169
|
+
|
|
170
|
+
tasks.get('arabic').locale.lang = 'ar';
|
|
171
|
+
refreshLocaleDirection('arabic');
|
|
172
|
+
assert.equal(arabic.formWrap.dir, 'rtl', 'task locale switch en -> ar must restore RTL');
|
|
173
|
+
assert.equal(arabic.popWrap.dir, 'rtl', 'task locale switch en -> ar must restore pop RTL');
|
|
174
|
+
|
|
175
|
+
// Global direction refresh changes the system root and inherited tasks, while explicit task locales remain isolated.
|
|
176
|
+
config.locale = 'ar';
|
|
177
|
+
refreshLocaleDirection();
|
|
178
|
+
assert.equal(elements.wrap.lang, 'ar', 'global locale switch must update #cg-wrap language');
|
|
179
|
+
assert.equal(elements.wrap.dir, 'rtl', 'global locale switch must update #cg-wrap direction');
|
|
180
|
+
assert.equal(inherited.formWrap.dir, 'rtl', 'task without an override must inherit global RTL');
|
|
181
|
+
assert.equal(inherited.popWrap.dir, 'rtl', 'inherited pop wrapper must inherit global RTL');
|
|
182
|
+
assert.equal(english.formWrap.dir, 'ltr', 'explicit English task must coexist with global Arabic RTL');
|
|
183
|
+
assert.equal(arabic.formWrap.dir, 'rtl', 'explicit Arabic task must coexist with global Arabic RTL');
|
|
184
|
+
|
|
185
|
+
config.locale = 'en';
|
|
186
|
+
refreshLocaleDirection();
|
|
187
|
+
assert.equal(elements.wrap.dir, 'ltr', 'global locale switch ar -> en must restore root LTR');
|
|
188
|
+
assert.equal(inherited.formWrap.dir, 'ltr', 'global locale switch ar -> en must restore inherited Form LTR');
|
|
189
|
+
assert.equal(inherited.popWrap.dir, 'ltr', 'global locale switch ar -> en must restore inherited pop LTR');
|
|
190
|
+
assert.equal(arabic.formWrap.dir, 'rtl', 'explicit Arabic task must remain RTL after global refresh');
|
|
191
|
+
|
|
192
|
+
dom.window.close();
|
|
193
|
+
console.log('RTL root, Form/pop isolation, task switching and global refresh checks passed from TypeScript source.');
|