@koishi-ce/client 1.0.3 → 1.1.0
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/README.md +2 -2
- package/app/home/home.vue +4 -1
- package/app/home/index.ts +2 -1
- package/app/home/welcome.vue +9 -26
- package/app/index.ts +8 -2
- package/app/layout/header.vue +12 -4
- package/app/layout/layout.vue +4 -1
- package/app/layout/menu-item.vue +3 -1
- package/app/settings/index.ts +3 -2
- package/app/settings/settings.vue +9 -7
- package/app/settings/theme.vue +4 -1
- package/app/status/loading.vue +7 -2
- package/app/theme/activity/button.vue +4 -1
- package/app/theme/activity/index.vue +12 -4
- package/app/theme/activity/item.vue +18 -4
- package/app/theme/activity/separator.vue +44 -12
- package/app/theme/blank.vue +7 -1
- package/app/theme/index.ts +7 -2
- package/app/theme/index.vue +6 -2
- package/app/theme/menu/menu-item.vue +10 -4
- package/app/theme/menu/menu.vue +9 -4
- package/client/components/chat/overlay.vue +38 -13
- package/client/components/common/k-hint.vue +3 -1
- package/client/components/dynamic.vue +2 -1
- package/client/components/icons/index.ts +7 -2
- package/client/components/index.ts +8 -2
- package/client/components/link.ts +5 -2
- package/client/components/perms.vue +22 -5
- package/client/components/slot.ts +19 -4
- package/client/context.ts +12 -4
- package/client/data.ts +57 -26
- package/client/index.ts +2 -1
- package/client/locales/de-DE.yml +47 -0
- package/client/locales/en-US.yml +47 -0
- package/client/locales/fr-FR.yml +47 -0
- package/client/locales/ja-JP.yml +47 -0
- package/client/locales/ru-RU.yml +47 -0
- package/client/locales/zh-CN.yml +47 -0
- package/client/locales/zh-TW.yml +47 -0
- package/client/plugins/action.ts +46 -13
- package/client/plugins/i18n.ts +118 -3
- package/client/plugins/loader.ts +67 -45
- package/client/plugins/router.ts +34 -11
- package/client/plugins/setting.ts +60 -16
- package/client/plugins/theme.ts +51 -11
- package/client/utils.ts +7 -2
- package/lib/bin.mjs +3 -3
- package/lib/{client-CaTn_gVG.mjs → client-C97iYPCr.mjs} +1 -1
- package/lib/index.d.ts +3 -4
- package/lib/index.mjs +1 -1
- package/lib/{src-DXaM1Kgh.mjs → src-BOakssV3.mjs} +2 -2
- package/package.json +3 -3
- package/src/bin.ts +10 -3
- package/src/index.ts +59 -15
- package/app/home/welcome.en-US.yml +0 -13
- package/app/home/welcome.zh-CN.yml +0 -13
package/client/plugins/router.ts
CHANGED
|
@@ -19,7 +19,11 @@ import {
|
|
|
19
19
|
ref,
|
|
20
20
|
toValue,
|
|
21
21
|
} from "vue";
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
createRouter,
|
|
24
|
+
createWebHistory,
|
|
25
|
+
START_LOCATION,
|
|
26
|
+
} from "vue-router";
|
|
23
27
|
import type { SlotOptions } from "../components";
|
|
24
28
|
import Overlay from "../components/chat/overlay.vue";
|
|
25
29
|
import type { Context } from "../context";
|
|
@@ -106,8 +110,15 @@ export class Activity {
|
|
|
106
110
|
this.options = options;
|
|
107
111
|
options.order ??= 0;
|
|
108
112
|
options.position ??= "top";
|
|
109
|
-
Object.assign(
|
|
110
|
-
|
|
113
|
+
Object.assign(
|
|
114
|
+
this,
|
|
115
|
+
omit(options, ["icon", "name", "desc", "disabled"]),
|
|
116
|
+
);
|
|
117
|
+
const {
|
|
118
|
+
path,
|
|
119
|
+
id = getActivityId(path),
|
|
120
|
+
component,
|
|
121
|
+
} = options;
|
|
111
122
|
// 路由挂载时把 activity 实例放进 meta,供导航守卫与标题栏取用
|
|
112
123
|
this._disposables.push(
|
|
113
124
|
ctx.$router.router.addRoute({
|
|
@@ -127,7 +138,9 @@ export class Activity {
|
|
|
127
138
|
/** 若存在待跳转目标且当前路由已能解析,则立即完成该跳转 */
|
|
128
139
|
handleUpdate() {
|
|
129
140
|
if (redirectTo.value) {
|
|
130
|
-
const location = this.ctx.$router.router.resolve(
|
|
141
|
+
const location = this.ctx.$router.router.resolve(
|
|
142
|
+
redirectTo.value,
|
|
143
|
+
);
|
|
131
144
|
if (location.matched.length) {
|
|
132
145
|
redirectTo.value = null;
|
|
133
146
|
this.ctx.$router.router.replace(location);
|
|
@@ -156,7 +169,8 @@ export class Activity {
|
|
|
156
169
|
*/
|
|
157
170
|
disabled() {
|
|
158
171
|
if (this.ctx.bail("activity", this)) return true;
|
|
159
|
-
if (!this.fields?.every((key) => store[key]))
|
|
172
|
+
if (!this.fields?.every((key) => store[key]))
|
|
173
|
+
return true;
|
|
160
174
|
if (this.when && !this.when()) return true;
|
|
161
175
|
if (this.options.disabled?.()) return true;
|
|
162
176
|
return false;
|
|
@@ -165,10 +179,13 @@ export class Activity {
|
|
|
165
179
|
dispose() {
|
|
166
180
|
this._disposables.forEach((dispose) => dispose());
|
|
167
181
|
// 若被卸载的恰是当前展示的页面:暂存目标路由,退回缓存的首页
|
|
168
|
-
const current =
|
|
182
|
+
const current =
|
|
183
|
+
this.ctx.$router.router.currentRoute.value;
|
|
169
184
|
if (current?.meta?.activity === this) {
|
|
170
185
|
redirectTo.value = current.fullPath;
|
|
171
|
-
this.ctx.$router.router.push(
|
|
186
|
+
this.ctx.$router.router.push(
|
|
187
|
+
this.ctx.$router.cache["home"] || "/",
|
|
188
|
+
);
|
|
172
189
|
}
|
|
173
190
|
return delete this.ctx.$router.pages[this.id];
|
|
174
191
|
}
|
|
@@ -203,7 +220,8 @@ export default class RouterService extends Service {
|
|
|
203
220
|
const initialTitle = document.title;
|
|
204
221
|
ctx.effect(() =>
|
|
205
222
|
this.router.afterEach((route) => {
|
|
206
|
-
const { name, fullPath } =
|
|
223
|
+
const { name, fullPath } =
|
|
224
|
+
this.router.currentRoute.value;
|
|
207
225
|
if (name) {
|
|
208
226
|
// 本应用的路由名均为字符串;reactive 收窄了索引签名,不含 symbol
|
|
209
227
|
this.cache[name as string] = fullPath;
|
|
@@ -211,7 +229,8 @@ export default class RouterService extends Service {
|
|
|
211
229
|
// 进入 activity 页面时更新标签页标题为 "页面名 | 原标题"
|
|
212
230
|
if (route.meta.activity) {
|
|
213
231
|
document.title = `${route.meta.activity.name}`;
|
|
214
|
-
if (initialTitle)
|
|
232
|
+
if (initialTitle)
|
|
233
|
+
document.title += ` | ${initialTitle}`;
|
|
215
234
|
}
|
|
216
235
|
}),
|
|
217
236
|
);
|
|
@@ -254,7 +273,9 @@ export default class RouterService extends Service {
|
|
|
254
273
|
*/
|
|
255
274
|
slot(options: SlotOptions) {
|
|
256
275
|
options.order ??= 0;
|
|
257
|
-
const component = this.ctx.wrapComponent(
|
|
276
|
+
const component = this.ctx.wrapComponent(
|
|
277
|
+
options.component,
|
|
278
|
+
);
|
|
258
279
|
if (component) options.component = component;
|
|
259
280
|
if (options.when) {
|
|
260
281
|
const { when } = options;
|
|
@@ -272,7 +293,9 @@ export default class RouterService extends Service {
|
|
|
272
293
|
|
|
273
294
|
/** 注册 activity 页面(自动挂到 vue-router 与侧栏);返回取消注册函数 */
|
|
274
295
|
page(options: Activity.Options) {
|
|
275
|
-
const component = this.ctx.wrapComponent(
|
|
296
|
+
const component = this.ctx.wrapComponent(
|
|
297
|
+
options.component,
|
|
298
|
+
);
|
|
276
299
|
if (component) options.component = component;
|
|
277
300
|
return this.ctx.effect(() => {
|
|
278
301
|
const activity = new Activity(this.ctx, options);
|
|
@@ -9,13 +9,28 @@
|
|
|
9
9
|
* (经所有插件 schema 补全后的完整配置),二者双向同步(见文末类注释)。
|
|
10
10
|
* 插件通过 `ctx.settings()` 注册设置分区、`ctx.schema()` 注册自定义控件。
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
type RemovableRef,
|
|
14
|
+
useLocalStorage,
|
|
15
|
+
} from "@vueuse/core";
|
|
13
16
|
import { type Dict, remove } from "cosmokit";
|
|
14
|
-
import {
|
|
15
|
-
|
|
17
|
+
import {
|
|
18
|
+
type Component,
|
|
19
|
+
computed,
|
|
20
|
+
type MaybeRefOrGetter,
|
|
21
|
+
markRaw,
|
|
22
|
+
reactive,
|
|
23
|
+
ref,
|
|
24
|
+
watch,
|
|
25
|
+
} from "vue";
|
|
26
|
+
import {
|
|
27
|
+
Schema,
|
|
28
|
+
SchemaBase,
|
|
29
|
+
} from "../../../components/client/index.ts";
|
|
16
30
|
import type { Config } from "..";
|
|
17
31
|
import type { Context } from "../context";
|
|
18
32
|
import { insert, type Ordered, Service } from "../utils";
|
|
33
|
+
import { pickMessages } from "./i18n";
|
|
19
34
|
|
|
20
35
|
declare module "../context" {
|
|
21
36
|
interface Context {
|
|
@@ -33,8 +48,8 @@ declare module "../context" {
|
|
|
33
48
|
interface SettingOptions extends Ordered {
|
|
34
49
|
/** 所属设置分区 id(空串为通用设置) */
|
|
35
50
|
id: string;
|
|
36
|
-
/**
|
|
37
|
-
title?: string
|
|
51
|
+
/** 分区标题(支持 getter,随界面语言实时切换) */
|
|
52
|
+
title?: MaybeRefOrGetter<string>;
|
|
38
53
|
disabled?: () => boolean;
|
|
39
54
|
/** 该分区的配置 schema(渲染为表单) */
|
|
40
55
|
schema?: Schema;
|
|
@@ -56,7 +71,10 @@ export let useStorage = <T extends object>(
|
|
|
56
71
|
__version__?: number | undefined;
|
|
57
72
|
};
|
|
58
73
|
initial.__version__ = version;
|
|
59
|
-
const storage = useLocalStorage(
|
|
74
|
+
const storage = useLocalStorage(
|
|
75
|
+
`koishi.console.${key}`,
|
|
76
|
+
initial,
|
|
77
|
+
);
|
|
60
78
|
if (storage.value.__version__ !== version) {
|
|
61
79
|
storage.value = initial;
|
|
62
80
|
}
|
|
@@ -87,7 +105,10 @@ export function createStorage<T extends object>(
|
|
|
87
105
|
if (storage.value.version !== version) {
|
|
88
106
|
storage.value = { version, data: initial };
|
|
89
107
|
} else if (!Array.isArray(storage.value.data)) {
|
|
90
|
-
storage.value.data = {
|
|
108
|
+
storage.value.data = {
|
|
109
|
+
...initial,
|
|
110
|
+
...storage.value.data,
|
|
111
|
+
};
|
|
91
112
|
}
|
|
92
113
|
return reactive<T>(storage.value["data"]);
|
|
93
114
|
}
|
|
@@ -138,17 +159,31 @@ export default class SettingService extends Service {
|
|
|
138
159
|
|
|
139
160
|
this.settings({
|
|
140
161
|
id: "",
|
|
141
|
-
title: "
|
|
162
|
+
title: () => this.ctx.$i18n.t("settings.general"),
|
|
142
163
|
order: 1000,
|
|
143
164
|
schema: Schema.object({
|
|
144
|
-
|
|
145
|
-
|
|
165
|
+
// 语言列表覆盖宿主词典支持的全部语种;
|
|
166
|
+
// 显示名采用各语言的本地名称(CLDR 惯例),无需翻译
|
|
167
|
+
locale: Schema.union([
|
|
168
|
+
Schema.const("zh-CN").description("简体中文"),
|
|
169
|
+
Schema.const("zh-TW").description("繁體中文"),
|
|
170
|
+
Schema.const("en-US").description("English"),
|
|
171
|
+
Schema.const("ja-JP").description("日本語"),
|
|
172
|
+
Schema.const("fr-FR").description("Français"),
|
|
173
|
+
Schema.const("de-DE").description("Deutsch"),
|
|
174
|
+
Schema.const("ru-RU").description("Русский"),
|
|
175
|
+
]).description("语言设置。"),
|
|
176
|
+
})
|
|
177
|
+
.description("通用设置")
|
|
178
|
+
.i18n(pickMessages("schema", "settings")),
|
|
146
179
|
});
|
|
147
180
|
|
|
148
181
|
// 汇总所有分区 schema 为一个相交对象,作为 resolved 的解释器
|
|
149
182
|
const schema = computed(() => {
|
|
150
183
|
const list: Schema[] = [];
|
|
151
|
-
for (const settings of Object.values(
|
|
184
|
+
for (const settings of Object.values(
|
|
185
|
+
ctx.internal.settings,
|
|
186
|
+
)) {
|
|
152
187
|
for (const options of settings) {
|
|
153
188
|
if (options.schema) {
|
|
154
189
|
list.push(options.schema);
|
|
@@ -186,7 +221,9 @@ export default class SettingService extends Service {
|
|
|
186
221
|
ctx.effect(() => () => stop?.());
|
|
187
222
|
|
|
188
223
|
// 原始存储或 schema 集合任一变化,都重新生成 resolved
|
|
189
|
-
ctx.effect(() =>
|
|
224
|
+
ctx.effect(() =>
|
|
225
|
+
watch(original, update, { deep: true }),
|
|
226
|
+
);
|
|
190
227
|
ctx.effect(() => watch(schema, update));
|
|
191
228
|
}
|
|
192
229
|
|
|
@@ -195,7 +232,9 @@ export default class SettingService extends Service {
|
|
|
195
232
|
* 返回取消注册函数。
|
|
196
233
|
*/
|
|
197
234
|
extendSchema(extension: SchemaBase.Extension) {
|
|
198
|
-
const component = this.ctx.wrapComponent(
|
|
235
|
+
const component = this.ctx.wrapComponent(
|
|
236
|
+
extension.component,
|
|
237
|
+
);
|
|
199
238
|
if (component) extension.component = component;
|
|
200
239
|
return this.ctx.effect(() => {
|
|
201
240
|
SchemaBase.extensions.add(extension);
|
|
@@ -207,14 +246,19 @@ export default class SettingService extends Service {
|
|
|
207
246
|
settings(options: SettingOptions) {
|
|
208
247
|
markRaw(options);
|
|
209
248
|
options.order ??= 0;
|
|
210
|
-
const component = this.ctx.wrapComponent(
|
|
249
|
+
const component = this.ctx.wrapComponent(
|
|
250
|
+
options.component,
|
|
251
|
+
);
|
|
211
252
|
if (component) options.component = component;
|
|
212
253
|
return this.ctx.effect(() => {
|
|
213
|
-
const list = (this.ctx.internal.settings[
|
|
254
|
+
const list = (this.ctx.internal.settings[
|
|
255
|
+
options.id
|
|
256
|
+
] ||= []);
|
|
214
257
|
insert(list, options);
|
|
215
258
|
return () => {
|
|
216
259
|
remove(list, options);
|
|
217
|
-
if (!list.length)
|
|
260
|
+
if (!list.length)
|
|
261
|
+
delete this.ctx.internal.settings[options.id];
|
|
218
262
|
};
|
|
219
263
|
});
|
|
220
264
|
}
|
package/client/plugins/theme.ts
CHANGED
|
@@ -11,10 +11,17 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { usePreferredDark } from "@vueuse/core";
|
|
13
13
|
import type { Dict } from "cosmokit";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
type Component,
|
|
16
|
+
computed,
|
|
17
|
+
markRaw,
|
|
18
|
+
reactive,
|
|
19
|
+
watchEffect,
|
|
20
|
+
} from "vue";
|
|
15
21
|
import { Schema } from "../../../components/client/index.ts";
|
|
16
22
|
import type { Context } from "../context";
|
|
17
23
|
import { Service } from "../utils";
|
|
24
|
+
import { pickMessages } from "./i18n";
|
|
18
25
|
import { useConfig } from "./setting";
|
|
19
26
|
|
|
20
27
|
declare module "../context" {
|
|
@@ -73,14 +80,38 @@ export default class ThemeService extends Service {
|
|
|
73
80
|
|
|
74
81
|
ctx.settings({
|
|
75
82
|
id: "appearance",
|
|
76
|
-
title: "
|
|
83
|
+
title: () => this.ctx.$i18n.t("settings.appearance"),
|
|
77
84
|
order: 900,
|
|
78
85
|
schema: Schema.object({
|
|
79
86
|
theme: Schema.object({
|
|
87
|
+
// const 选项的显示名不受 .i18n() 递归覆盖(其词典
|
|
88
|
+
// 递归会滤掉 $ 键),故以 Dict 形态经 extra 写入
|
|
89
|
+
// meta.description(description() 方法仅收 string)
|
|
80
90
|
mode: Schema.union([
|
|
81
|
-
Schema.const("auto").
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
Schema.const("auto").extra(
|
|
92
|
+
"description",
|
|
93
|
+
pickMessages<string>(
|
|
94
|
+
"schema",
|
|
95
|
+
"theme",
|
|
96
|
+
"mode-auto",
|
|
97
|
+
),
|
|
98
|
+
),
|
|
99
|
+
Schema.const("dark").extra(
|
|
100
|
+
"description",
|
|
101
|
+
pickMessages<string>(
|
|
102
|
+
"schema",
|
|
103
|
+
"theme",
|
|
104
|
+
"mode-dark",
|
|
105
|
+
),
|
|
106
|
+
),
|
|
107
|
+
Schema.const("light").extra(
|
|
108
|
+
"description",
|
|
109
|
+
pickMessages<string>(
|
|
110
|
+
"schema",
|
|
111
|
+
"theme",
|
|
112
|
+
"mode-light",
|
|
113
|
+
),
|
|
114
|
+
),
|
|
84
115
|
])
|
|
85
116
|
.default("auto")
|
|
86
117
|
.description("主题偏好。"),
|
|
@@ -93,18 +124,22 @@ export default class ThemeService extends Service {
|
|
|
93
124
|
.default("default-light")
|
|
94
125
|
.description("浅色主题。"),
|
|
95
126
|
}).description("主题设置"),
|
|
96
|
-
}),
|
|
127
|
+
}).i18n(pickMessages("schema", "theme")),
|
|
97
128
|
});
|
|
98
129
|
|
|
99
130
|
ctx.effect(() =>
|
|
100
131
|
watchEffect(
|
|
101
132
|
() => {
|
|
102
133
|
if (!config.value.theme) return;
|
|
103
|
-
const root =
|
|
134
|
+
const root =
|
|
135
|
+
window.document.querySelector("html");
|
|
104
136
|
if (!root) return;
|
|
105
137
|
// 把当前主题名写到 <html theme="...">,并同步 dark 类;
|
|
106
138
|
// 主题样式与深色变量均由 CSS 依据这两个标记选择
|
|
107
|
-
root.setAttribute(
|
|
139
|
+
root.setAttribute(
|
|
140
|
+
"theme",
|
|
141
|
+
config.value.theme[colorMode.value],
|
|
142
|
+
);
|
|
108
143
|
if (colorMode.value === "dark") {
|
|
109
144
|
root.classList.add("dark");
|
|
110
145
|
} else {
|
|
@@ -123,16 +158,21 @@ export default class ThemeService extends Service {
|
|
|
123
158
|
*/
|
|
124
159
|
theme(options: ThemeOptions) {
|
|
125
160
|
markRaw(options);
|
|
126
|
-
for (const [type, component] of Object.entries(
|
|
161
|
+
for (const [type, component] of Object.entries(
|
|
162
|
+
options.components || {},
|
|
163
|
+
)) {
|
|
127
164
|
this.ctx.slot({
|
|
128
165
|
type,
|
|
129
|
-
disabled: () =>
|
|
166
|
+
disabled: () =>
|
|
167
|
+
config.value.theme[colorMode.value] !==
|
|
168
|
+
options.id,
|
|
130
169
|
component,
|
|
131
170
|
});
|
|
132
171
|
}
|
|
133
172
|
return this.ctx.effect(() => {
|
|
134
173
|
this.ctx.internal.themes[options.id] = options;
|
|
135
|
-
return () =>
|
|
174
|
+
return () =>
|
|
175
|
+
delete this.ctx.internal.themes[options.id];
|
|
136
176
|
});
|
|
137
177
|
}
|
|
138
178
|
}
|
package/client/utils.ts
CHANGED
|
@@ -24,13 +24,18 @@ export interface Ordered {
|
|
|
24
24
|
* 按 order 升序将条目插入有序列表(相同 order 的后者排在后面)。
|
|
25
25
|
* 同时 markRaw 标记条目,避免其被 Vue 深度代理。
|
|
26
26
|
*/
|
|
27
|
-
export function insert<T extends Ordered>(
|
|
27
|
+
export function insert<T extends Ordered>(
|
|
28
|
+
list: T[],
|
|
29
|
+
item: T,
|
|
30
|
+
) {
|
|
28
31
|
markRaw(item);
|
|
29
32
|
// order 为可选属性:任一侧缺失(undefined)时数值比较结果恒为 false,
|
|
30
33
|
// 与原实现(直接比较)在所有输入下的求值结果一致,这里显式判空以通过严格空检查
|
|
31
34
|
const index = list.findIndex(
|
|
32
35
|
(a) =>
|
|
33
|
-
a.order !== undefined &&
|
|
36
|
+
a.order !== undefined &&
|
|
37
|
+
item.order !== undefined &&
|
|
38
|
+
a.order < item.order,
|
|
34
39
|
);
|
|
35
40
|
if (index >= 0) {
|
|
36
41
|
list.splice(index, 0, item);
|
package/lib/bin.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as build } from "./src-
|
|
2
|
+
import { t as build } from "./src-BOakssV3.mjs";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { resolve } from "node:path";
|
|
5
5
|
//#endregion
|
|
@@ -16,7 +16,7 @@ import { resolve } from "node:path";
|
|
|
16
16
|
const { version } = {
|
|
17
17
|
name: "@koishi-ce/client",
|
|
18
18
|
description: "Koishi Console Client",
|
|
19
|
-
version: "1.0
|
|
19
|
+
version: "1.1.0",
|
|
20
20
|
type: "module",
|
|
21
21
|
main: "client/index.ts",
|
|
22
22
|
exports: {
|
|
@@ -104,7 +104,7 @@ async function main() {
|
|
|
104
104
|
await build(resolve(process.cwd(), target));
|
|
105
105
|
return;
|
|
106
106
|
}
|
|
107
|
-
await (await import("./client-
|
|
107
|
+
await (await import("./client-C97iYPCr.mjs")).default();
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
110
|
console.error(`Unknown command ${JSON.stringify(command)}.`);
|
package/lib/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import * as vite from "vite";
|
|
|
6
6
|
* @param root 插件目录(无 `client/` 子目录时视为该插件没有前端,直接跳过)
|
|
7
7
|
* @param config 额外的 vite 配置,逐层合并覆盖下方默认值
|
|
8
8
|
*/
|
|
9
|
-
declare function build(root: string, config?: vite.UserConfig): Promise<void>;
|
|
9
|
+
export declare function build(root: string, config?: vite.UserConfig): Promise<void>;
|
|
10
10
|
/**
|
|
11
11
|
* 创建开发模式下的 vite dev server(middlewareMode,不监听独立端口,
|
|
12
12
|
* 由宿主 HTTP 服务挂载到 `/vite/` 前缀下)。
|
|
@@ -15,6 +15,5 @@ declare function build(root: string, config?: vite.UserConfig): Promise<void>;
|
|
|
15
15
|
* @param config 额外的 vite 配置
|
|
16
16
|
* @returns vite 的 ViteDevServer 实例
|
|
17
17
|
*/
|
|
18
|
-
declare function createServer(baseDir: string, config?: vite.InlineConfig): Promise<vite.ViteDevServer>;
|
|
19
|
-
//#endregion
|
|
20
|
-
export { build, createServer };
|
|
18
|
+
export declare function createServer(baseDir: string, config?: vite.InlineConfig): Promise<vite.ViteDevServer>;
|
|
19
|
+
//#endregion
|
package/lib/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as createServer, t as build } from "./src-
|
|
1
|
+
import { n as createServer, t as build } from "./src-BOakssV3.mjs";
|
|
2
2
|
export { build, createServer };
|
|
@@ -82,7 +82,7 @@ async function build(root, config = {}) {
|
|
|
82
82
|
}
|
|
83
83
|
},
|
|
84
84
|
plugins: [
|
|
85
|
-
vue(),
|
|
85
|
+
vue({ template: { compilerOptions: { comments: false } } }),
|
|
86
86
|
yaml(),
|
|
87
87
|
(await import("unocss/vite")).default({ presets: [(await import("unocss/preset-mini")).default({ preflight: false })] })
|
|
88
88
|
],
|
|
@@ -130,7 +130,7 @@ async function createServer(baseDir, config = {}) {
|
|
|
130
130
|
fs: { allow: [vite.searchForWorkspaceRoot(baseDir)] }
|
|
131
131
|
},
|
|
132
132
|
plugins: [
|
|
133
|
-
vue(),
|
|
133
|
+
vue({ template: { compilerOptions: { comments: false } } }),
|
|
134
134
|
yaml(),
|
|
135
135
|
(await import("unocss/vite")).default({ presets: [(await import("unocss/preset-mini")).default({ preflight: false })] })
|
|
136
136
|
],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@koishi-ce/client",
|
|
3
3
|
"description": "Koishi Console Client",
|
|
4
|
-
"version": "1.0
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "client/index.ts",
|
|
7
7
|
"exports": {
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"vue-router": "^5.2.0"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
|
-
"@koishi-ce/koishi": "^1.0.
|
|
68
|
-
"@koishi-ce/plugin-console": "^1.0
|
|
67
|
+
"@koishi-ce/koishi": "^1.0.8",
|
|
68
|
+
"@koishi-ce/plugin-console": "^1.1.0"
|
|
69
69
|
}
|
|
70
70
|
}
|
package/src/bin.ts
CHANGED
|
@@ -36,7 +36,11 @@ Options:
|
|
|
36
36
|
|
|
37
37
|
async function main() {
|
|
38
38
|
const [command, ...args] = process.argv.slice(2);
|
|
39
|
-
if (
|
|
39
|
+
if (
|
|
40
|
+
command === undefined ||
|
|
41
|
+
command === "-h" ||
|
|
42
|
+
command === "--help"
|
|
43
|
+
) {
|
|
40
44
|
console.log(help);
|
|
41
45
|
return;
|
|
42
46
|
}
|
|
@@ -47,7 +51,8 @@ async function main() {
|
|
|
47
51
|
if (command === "build") {
|
|
48
52
|
// root 缺省时:cwd 是插件目录则构建它,否则视为宿主总装
|
|
49
53
|
const root = args.find((arg) => !arg.startsWith("-"));
|
|
50
|
-
const target =
|
|
54
|
+
const target =
|
|
55
|
+
root ?? (existsSync("client") ? "." : undefined);
|
|
51
56
|
if (target !== undefined) {
|
|
52
57
|
await build(resolve(process.cwd(), target));
|
|
53
58
|
return;
|
|
@@ -56,7 +61,9 @@ async function main() {
|
|
|
56
61
|
await host.default();
|
|
57
62
|
return;
|
|
58
63
|
}
|
|
59
|
-
console.error(
|
|
64
|
+
console.error(
|
|
65
|
+
`Unknown command ${JSON.stringify(command)}.`,
|
|
66
|
+
);
|
|
60
67
|
console.log(help);
|
|
61
68
|
process.exitCode = 1;
|
|
62
69
|
}
|
package/src/index.ts
CHANGED
|
@@ -33,21 +33,32 @@ interface BuildResult {
|
|
|
33
33
|
// 将全部工作区包名映射到其源码目录,行为对齐根 tsconfig 的 paths 别名。
|
|
34
34
|
// 没有被任何工作区包依赖的插件(如 plugin-logger)不会出现在 node_modules
|
|
35
35
|
// 的链接里,bundler 无法按包名解析,必须显式提供这层映射。
|
|
36
|
-
async function collectWorkspaceAliases(): Promise<
|
|
36
|
+
async function collectWorkspaceAliases(): Promise<
|
|
37
|
+
Record<string, string>
|
|
38
|
+
> {
|
|
37
39
|
// 源码形态(src/)与产物形态(lib/)都在包根下一级,上跳四级到仓库根一致
|
|
38
|
-
const repoRoot = resolve(
|
|
39
|
-
|
|
40
|
+
const repoRoot = resolve(
|
|
41
|
+
import.meta.dir,
|
|
42
|
+
"../../../..",
|
|
43
|
+
).replace(/\\/g, "/");
|
|
44
|
+
const manifest = await Bun.file(
|
|
45
|
+
`${repoRoot}/package.json`,
|
|
46
|
+
).json();
|
|
40
47
|
const aliases: Record<string, string> = {};
|
|
41
48
|
for (const pattern of manifest.workspaces ?? []) {
|
|
42
49
|
// scanSync 产出的相对路径在 Windows 上是反斜杠,统一归一化为正斜杠
|
|
43
|
-
const files = new Bun.Glob(
|
|
50
|
+
const files = new Bun.Glob(
|
|
51
|
+
`${pattern}/package.json`,
|
|
52
|
+
).scanSync({
|
|
44
53
|
cwd: repoRoot,
|
|
45
54
|
});
|
|
46
55
|
for (const file of files) {
|
|
47
56
|
const rel = file.replaceAll("\\", "/");
|
|
48
57
|
const dir = `${repoRoot}/${rel.slice(0, -"/package.json".length)}`;
|
|
49
58
|
try {
|
|
50
|
-
const { name } = await Bun.file(
|
|
59
|
+
const { name } = await Bun.file(
|
|
60
|
+
`${dir}/package.json`,
|
|
61
|
+
).json();
|
|
51
62
|
if (!name) continue;
|
|
52
63
|
// 控制台前端语境下,裸包名对到浏览器端入口(替代上游 lib 的 browser
|
|
53
64
|
// 导出条件);`<name>/src` 子路径对到源码目录,供共享代码引用;
|
|
@@ -56,9 +67,13 @@ async function collectWorkspaceAliases(): Promise<Record<string, string>> {
|
|
|
56
67
|
// 上游与 npm 产物的 exports 均未声明它,同样靠仓库内别名解析)。
|
|
57
68
|
// 子路径键必须先插入——别名解析按插入序取首个命中项
|
|
58
69
|
const clientEntry = `${dir}/client/index.ts`;
|
|
59
|
-
if (existsSync(`${dir}/src`))
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
if (existsSync(`${dir}/src`))
|
|
71
|
+
aliases[`${name}/src`] = `${dir}/src`;
|
|
72
|
+
if (existsSync(clientEntry))
|
|
73
|
+
aliases[`${name}/client`] = clientEntry;
|
|
74
|
+
aliases[name] = existsSync(clientEntry)
|
|
75
|
+
? clientEntry
|
|
76
|
+
: `${dir}/src`;
|
|
62
77
|
} catch {}
|
|
63
78
|
}
|
|
64
79
|
}
|
|
@@ -72,7 +87,10 @@ const workspaceAliases = await collectWorkspaceAliases();
|
|
|
72
87
|
// 类型面由根 tsconfig.client.json 的 paths 解析到 schemastery-vue-client.ts
|
|
73
88
|
const runtimeShimPath = (
|
|
74
89
|
workspaceAliases["@koishi-ce/components"] ?? ""
|
|
75
|
-
).replace(
|
|
90
|
+
).replace(
|
|
91
|
+
/client\/index\.ts$/,
|
|
92
|
+
"client/schemastery-vue-runtime.ts",
|
|
93
|
+
);
|
|
76
94
|
|
|
77
95
|
/**
|
|
78
96
|
* 构建单个 webui 插件的前端产物。
|
|
@@ -80,7 +98,10 @@ const runtimeShimPath = (
|
|
|
80
98
|
* @param root 插件目录(无 `client/` 子目录时视为该插件没有前端,直接跳过)
|
|
81
99
|
* @param config 额外的 vite 配置,逐层合并覆盖下方默认值
|
|
82
100
|
*/
|
|
83
|
-
export async function build(
|
|
101
|
+
export async function build(
|
|
102
|
+
root: string,
|
|
103
|
+
config: vite.UserConfig = {},
|
|
104
|
+
) {
|
|
84
105
|
if (!existsSync(`${root}/client`)) return;
|
|
85
106
|
|
|
86
107
|
// 插件可自带 `build/client.ts` 导出额外的 vite 配置覆盖下方默认值
|
|
@@ -88,7 +109,9 @@ export async function build(root: string, config: vite.UserConfig = {}) {
|
|
|
88
109
|
// 如 analytics 的 fuck-echarts 符号遮蔽修补
|
|
89
110
|
const overridePath = `${root}/build/client.ts`;
|
|
90
111
|
if (existsSync(overridePath)) {
|
|
91
|
-
const mod = await import(
|
|
112
|
+
const mod = await import(
|
|
113
|
+
pathToFileURL(overridePath).href
|
|
114
|
+
);
|
|
92
115
|
config = vite.mergeConfig(config, mod.default ?? mod);
|
|
93
116
|
}
|
|
94
117
|
|
|
@@ -124,7 +147,9 @@ export async function build(root: string, config: vite.UserConfig = {}) {
|
|
|
124
147
|
// 仓库侧无法根治,直接静默
|
|
125
148
|
onwarn(warning, warn) {
|
|
126
149
|
if (
|
|
127
|
-
warning.message.includes(
|
|
150
|
+
warning.message.includes(
|
|
151
|
+
"is being imported multiple times",
|
|
152
|
+
)
|
|
128
153
|
) {
|
|
129
154
|
return;
|
|
130
155
|
}
|
|
@@ -144,7 +169,16 @@ export async function build(root: string, config: vite.UserConfig = {}) {
|
|
|
144
169
|
},
|
|
145
170
|
},
|
|
146
171
|
plugins: [
|
|
147
|
-
|
|
172
|
+
// 钉死剥离模板注释:注释写在 template 根元素之前时,SFC 会被
|
|
173
|
+
// 编译成多根 fragment,Vue 随之禁用 attribute 透传(外部传入的
|
|
174
|
+
// class 落不到 svg 上,侧栏图标因此丢掉尺寸类);生产语义本就
|
|
175
|
+
// 应剥注释,这里显式钉死,避免随 NODE_ENV 漂移。dev server
|
|
176
|
+
// 同步钉死,保证开发态与产物行为一致
|
|
177
|
+
vue({
|
|
178
|
+
template: {
|
|
179
|
+
compilerOptions: { comments: false },
|
|
180
|
+
},
|
|
181
|
+
}),
|
|
148
182
|
yaml(),
|
|
149
183
|
(
|
|
150
184
|
await import("unocss/vite")
|
|
@@ -171,7 +205,8 @@ export async function build(root: string, config: vite.UserConfig = {}) {
|
|
|
171
205
|
// 源码发布的组件库),其内部以 npm 名引用组件库;重定向到
|
|
172
206
|
// 本仓库同版本(1.5.22)components 源码,避免 npm 版整套
|
|
173
207
|
// 组件库被打进插件产物
|
|
174
|
-
"@koishijs/components":
|
|
208
|
+
"@koishijs/components":
|
|
209
|
+
workspaceAliases["@koishi-ce/components"],
|
|
175
210
|
// 虚拟子路径的运行时载体(补齐真实包缺失的 SchemaBase
|
|
176
211
|
// 具名导出);类型面由 tsconfig.client.json 的 paths
|
|
177
212
|
// 解析到 schemastery-vue-client.ts
|
|
@@ -242,7 +277,16 @@ export async function createServer(
|
|
|
242
277
|
},
|
|
243
278
|
},
|
|
244
279
|
plugins: [
|
|
245
|
-
|
|
280
|
+
// 钉死剥离模板注释:注释写在 template 根元素之前时,SFC 会被
|
|
281
|
+
// 编译成多根 fragment,Vue 随之禁用 attribute 透传(外部传入的
|
|
282
|
+
// class 落不到 svg 上,侧栏图标因此丢掉尺寸类);生产语义本就
|
|
283
|
+
// 应剥注释,这里显式钉死,避免随 NODE_ENV 漂移。dev server
|
|
284
|
+
// 同步钉死,保证开发态与产物行为一致
|
|
285
|
+
vue({
|
|
286
|
+
template: {
|
|
287
|
+
compilerOptions: { comments: false },
|
|
288
|
+
},
|
|
289
|
+
}),
|
|
246
290
|
yaml(),
|
|
247
291
|
(await import("unocss/vite")).default({
|
|
248
292
|
presets: [
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
-
# Copyright (c) 2019-present Shigma and Koishijs contributors.
|
|
3
|
-
# Copyright (c) 2026-present Koishi-CE contributors.
|
|
4
|
-
|
|
5
|
-
title: Welcome to Koishi!
|
|
6
|
-
description: Your chatbot is ready to go. 点击下面的任一选项,开启你的 Koishi 之旅吧。
|
|
7
|
-
action:
|
|
8
|
-
docs:
|
|
9
|
-
title: Documentation
|
|
10
|
-
description: The official documentation contains everything you want to know.
|
|
11
|
-
forum:
|
|
12
|
-
title: Forum
|
|
13
|
-
description: If you run into problems, you can always ask for help on the forum.
|