@koishi-ce/client 1.0.2 → 1.0.4

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.
Files changed (115) hide show
  1. package/README.md +56 -0
  2. package/app/home/home.vue +4 -0
  3. package/app/home/index.ts +4 -0
  4. package/app/home/welcome.en-US.yml +4 -0
  5. package/app/home/welcome.vue +16 -6
  6. package/app/home/welcome.zh-CN.yml +4 -0
  7. package/app/index.scss +4 -0
  8. package/app/index.ts +12 -2
  9. package/app/layout/header.vue +16 -4
  10. package/app/layout/index.ts +4 -0
  11. package/app/layout/layout.vue +8 -1
  12. package/app/layout/menu-item.vue +7 -1
  13. package/app/settings/index.ts +4 -0
  14. package/app/settings/settings.vue +10 -4
  15. package/app/settings/theme.vue +8 -1
  16. package/app/shims.d.ts +3 -0
  17. package/app/status/index.ts +4 -0
  18. package/app/status/loading.vue +8 -1
  19. package/app/status/status.vue +4 -0
  20. package/app/styles/element.scss +4 -0
  21. package/app/styles/hc.scss +4 -0
  22. package/app/styles/index.scss +4 -0
  23. package/app/styles/index.ts +4 -0
  24. package/app/styles/layout.scss +4 -0
  25. package/app/theme/activity/button.vue +8 -1
  26. package/app/theme/activity/index.vue +16 -4
  27. package/app/theme/activity/item.vue +22 -4
  28. package/app/theme/activity/separator.vue +48 -12
  29. package/app/theme/activity/utils.ts +4 -0
  30. package/app/theme/blank.vue +4 -0
  31. package/app/theme/index.ts +9 -1
  32. package/app/theme/index.vue +7 -1
  33. package/app/theme/menu/index.vue +4 -0
  34. package/app/theme/menu/menu-item.vue +14 -4
  35. package/app/theme/menu/menu.vue +13 -4
  36. package/app/theme/status.vue +4 -0
  37. package/client/components/chat/image.vue +4 -0
  38. package/client/components/chat/overlay.vue +34 -8
  39. package/client/components/chat/utils.ts +4 -0
  40. package/client/components/common/index.ts +4 -0
  41. package/client/components/common/k-button.vue +4 -0
  42. package/client/components/common/k-hint.vue +7 -1
  43. package/client/components/common/k-tab.vue +4 -0
  44. package/client/components/dynamic.vue +6 -1
  45. package/client/components/icons/activity/default.vue +4 -0
  46. package/client/components/icons/activity/ellipsis.vue +4 -0
  47. package/client/components/icons/activity/home.vue +4 -0
  48. package/client/components/icons/activity/moon.vue +4 -0
  49. package/client/components/icons/activity/settings.vue +4 -0
  50. package/client/components/icons/activity/sun.vue +4 -0
  51. package/client/components/icons/index.ts +11 -2
  52. package/client/components/icons/style.scss +4 -0
  53. package/client/components/icons/svg/arrow-left.vue +4 -0
  54. package/client/components/icons/svg/arrow-right.vue +4 -0
  55. package/client/components/icons/svg/box-open.vue +4 -0
  56. package/client/components/icons/svg/check-full.vue +4 -0
  57. package/client/components/icons/svg/chevron-down.vue +4 -0
  58. package/client/components/icons/svg/chevron-left.vue +4 -0
  59. package/client/components/icons/svg/chevron-right.vue +4 -0
  60. package/client/components/icons/svg/chevron-up.vue +4 -0
  61. package/client/components/icons/svg/clipboard-list.vue +4 -0
  62. package/client/components/icons/svg/edit.vue +4 -0
  63. package/client/components/icons/svg/exclamation-full.vue +4 -0
  64. package/client/components/icons/svg/expand.vue +4 -0
  65. package/client/components/icons/svg/file-archive.vue +4 -0
  66. package/client/components/icons/svg/filter.vue +4 -0
  67. package/client/components/icons/svg/github.vue +4 -0
  68. package/client/components/icons/svg/gitlab.vue +4 -0
  69. package/client/components/icons/svg/info-full.vue +4 -0
  70. package/client/components/icons/svg/koishi.vue +4 -0
  71. package/client/components/icons/svg/link.vue +4 -0
  72. package/client/components/icons/svg/paper-plane.vue +4 -0
  73. package/client/components/icons/svg/question-empty.vue +4 -0
  74. package/client/components/icons/svg/redo.vue +4 -0
  75. package/client/components/icons/svg/search-minus.vue +4 -0
  76. package/client/components/icons/svg/search-plus.vue +4 -0
  77. package/client/components/icons/svg/search.vue +4 -0
  78. package/client/components/icons/svg/star-empty.vue +4 -0
  79. package/client/components/icons/svg/star-full.vue +4 -0
  80. package/client/components/icons/svg/start.vue +4 -0
  81. package/client/components/icons/svg/tag.vue +4 -0
  82. package/client/components/icons/svg/times-full.vue +4 -0
  83. package/client/components/icons/svg/tools.vue +4 -0
  84. package/client/components/icons/svg/undo.vue +4 -0
  85. package/client/components/icons/svg/user.vue +4 -0
  86. package/client/components/index.ts +12 -2
  87. package/client/components/layout/card.vue +4 -0
  88. package/client/components/layout/content.vue +4 -0
  89. package/client/components/layout/empty.vue +4 -0
  90. package/client/components/layout/index.ts +4 -0
  91. package/client/components/layout/tab-group.vue +4 -0
  92. package/client/components/layout/tab-item.vue +4 -0
  93. package/client/components/link.ts +9 -2
  94. package/client/components/perms.vue +26 -5
  95. package/client/components/slot.ts +23 -4
  96. package/client/context.ts +16 -4
  97. package/client/data.ts +61 -26
  98. package/client/index.ts +6 -1
  99. package/client/plugins/action.ts +50 -13
  100. package/client/plugins/i18n.ts +4 -0
  101. package/client/plugins/loader.ts +71 -45
  102. package/client/plugins/router.ts +38 -11
  103. package/client/plugins/setting.ts +49 -12
  104. package/client/plugins/theme.ts +25 -6
  105. package/client/shims.d.ts +3 -0
  106. package/client/utils.ts +11 -2
  107. package/global.d.ts +4 -0
  108. package/lib/bin.mjs +7 -3
  109. package/lib/{client-CaTn_gVG.mjs → client-C97iYPCr.mjs} +1 -1
  110. package/lib/index.mjs +1 -1
  111. package/lib/{src-lTfcJ1xA.mjs → src-BOakssV3.mjs} +8 -2
  112. package/package.json +6 -2
  113. package/src/bin.ts +13 -3
  114. package/src/index.ts +72 -14
  115. package/src/yaml.ts +3 -0
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @koishi-ce/client
2
+
3
+ **简体中文** | [English](#english)
4
+
5
+ Koishi 控制台的浏览器端运行时与组件基座,移植自上游 [koishijs/webui](https://github.com/koishijs/webui) 的 `packages/client`。它承载控制台前端的全部基础设施——根 Context、服务与插件系统、路由与活动栏、状态管理、主题、动作与菜单——同时提供把各控制台插件的前端组装为可部署产物的编程式构建器。
6
+
7
+ 与 [`@koishi-ce/plugin-console`](https://github.com/Koishi-CE/koishi/tree/main/plugins/webui/console)(node 侧宿主)的分工:本包运行在浏览器,是前端基座;plugin-console 运行在 node,负责 WebSocket 通道与静态资源托管。宿主总装产物直接写入 plugin-console 的 `dist` 目录。
8
+
9
+ ## 主要导出
10
+
11
+ - `root` 与 `Context`:唯一根上下文实例与其类型;
12
+ - `defineExtension`:控制台插件前端扩展的定义入口;
13
+ - `Service`:前端侧服务基类;
14
+ - 路由与活动:`RouterService`、`Activity`、`redirectTo`;
15
+ - 状态:`store`(reactive 全局状态)及 `socket` / `send` / `receive` / `connect`;
16
+ - 设置与主题:`createStorage`、`useConfig`、`SettingService`、`useColorMode`、`ThemeService`;
17
+ - 动作与菜单:`useMenu` 与 ActionService(动作面板、菜单注册、全局快捷键分发);
18
+ - 国际化:I18nService;
19
+ - element-plus 的 `loading` / `message` / `messageBox` 服务封装;
20
+ - `Satori` / `Universal` 命名空间与 `ScopeStatus` 常量。
21
+
22
+ ## 构建器
23
+
24
+ 本包没有独立的运行时发布形态,而是作为源码被各前端工程消费:
25
+
26
+ - CLI:`koishi-console build [root]`(`src/bin.ts`)——带 root 时构建单个插件的前端,否则执行宿主控制台总装(产物硬编码到 `plugins/webui/console/dist`,含共享 chunk 与样式);
27
+ - 编程式:`build(root, config)` 构建单个插件前端,`createServer(baseDir)` 创建 Vite 开发中间件;
28
+ - `collectWorkspaceAliases()`:读取根 package.json 的 workspaces,为每个工作区包名建立指向其 `client/index.ts` 的别名——未被依赖的 workspace 包不会出现在 node_modules 链接里,必须显式映射才能被 bundler 解析。
29
+
30
+ ## 许可证
31
+
32
+ [AGPL-3.0](https://github.com/Koishi-CE/koishi/blob/main/LICENSES/AGPL-3.0.txt)。本包是上游 koishijs/webui 的社区再分发,版权归 Shigma 及 Koishijs 贡献者(上游)与 Koishi-CE 贡献者,见 [NOTICE](https://github.com/Koishi-CE/koishi/blob/main/NOTICE)。
33
+
34
+ ---
35
+
36
+ ## English
37
+
38
+ The browser-side runtime and component foundation of the Koishi console, ported from `packages/client` of the upstream [koishijs/webui](https://github.com/koishijs/webui). It hosts the entire frontend infrastructure — the root Context, services and plugin system, router and activity bar, state management, themes, actions and menus — and ships a programmatic builder that assembles console plugin frontends into deployable bundles.
39
+
40
+ Division of labor with [`@koishi-ce/plugin-console`](https://github.com/Koishi-CE/koishi/tree/main/plugins/webui/console) (the node-side host): this package runs in the browser as the frontend foundation, while plugin-console runs on node serving the WebSocket channel and static assets. Host assembly output is written straight into plugin-console's `dist` directory.
41
+
42
+ ## Key exports
43
+
44
+ `root` and `Context` (the single root context), `defineExtension` (entry for console plugin frontends), the frontend `Service` base, `RouterService` / `Activity` / `redirectTo`, the reactive `store` with `socket` / `send` / `receive` / `connect`, settings and theming (`createStorage`, `useConfig`, `SettingService`, `useColorMode`, `ThemeService`), actions and menus (`useMenu`, ActionService with global shortcut dispatch), I18nService, element-plus service wrappers (`loading` / `message` / `messageBox`), plus the `Satori` / `Universal` namespaces and `ScopeStatus`.
45
+
46
+ ## Builder
47
+
48
+ The package is consumed as source code rather than shipped as a standalone runtime:
49
+
50
+ - CLI: `koishi-console build [root]` — builds a single plugin frontend when a root is given, otherwise performs the host console assembly (output hardcoded to `plugins/webui/console/dist`).
51
+ - Programmatic: `build(root, config)` and `createServer(baseDir)` (a Vite dev middleware).
52
+ - `collectWorkspaceAliases()` — maps every workspace package to its `client/index.ts`; workspace packages absent from node_modules links can only be resolved through this explicit alias map.
53
+
54
+ ## License
55
+
56
+ [AGPL-3.0](https://github.com/Koishi-CE/koishi/blob/main/LICENSES/AGPL-3.0.txt). Community redistribution of upstream koishijs/webui; copyright Shigma and Koishijs contributors (upstream) and Koishi-CE contributors — see [NOTICE](https://github.com/Koishi-CE/koishi/blob/main/NOTICE).
package/app/home/home.vue CHANGED
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  控制台首页:WebSocket 就绪后渲染 "home" 插槽内容(默认为欢迎卡片),
3
7
  未连接时显示连接提示卡片。
package/app/home/index.ts CHANGED
@@ -1,3 +1,7 @@
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
+
1
5
  /**
2
6
  * 首页插件:将 welcome 欢迎页注册为根路由 "/"。
3
7
  * order 取极大值(活动栏 top 组渲染时按 order 逆序输出,故首页位于最顶部)。
@@ -1,3 +1,7 @@
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
+
1
5
  title: Welcome to Koishi!
2
6
  description: Your chatbot is ready to go. 点击下面的任一选项,开启你的 Koishi 之旅吧。
3
7
  action:
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  欢迎卡片:文档 / 社区论坛等入口链接。
3
7
  自带 zh-CN 与 en-US 两套文案(局部 vue-i18n 实例,独立于全局语言包),
@@ -41,12 +45,18 @@ const { t, setLocaleMessage } = useI18n({
41
45
 
42
46
  // 开发模式下 yml 文案改动即时热替换,无需整页刷新
43
47
  if (import.meta.hot) {
44
- import.meta.hot.accept("./welcome.zh-CN.yml", (module) => {
45
- setLocaleMessage("zh-CN", module.default);
46
- });
47
- import.meta.hot.accept("./welcome.en-US.yml", (module) => {
48
- setLocaleMessage("en-US", module.default);
49
- });
48
+ import.meta.hot.accept(
49
+ "./welcome.zh-CN.yml",
50
+ (module) => {
51
+ setLocaleMessage("zh-CN", module.default);
52
+ },
53
+ );
54
+ import.meta.hot.accept(
55
+ "./welcome.en-US.yml",
56
+ (module) => {
57
+ setLocaleMessage("en-US", module.default);
58
+ },
59
+ );
50
60
  }
51
61
  </script>
52
62
 
@@ -1,3 +1,7 @@
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
+
1
5
  title: 欢迎使用 Koishi!
2
6
  description: 你的机器人已经准备就绪。点击下面的任一选项,开启你的 Koishi 之旅吧。
3
7
  action:
package/app/index.scss CHANGED
@@ -1,3 +1,7 @@
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
+
1
5
  body {
2
6
  margin: 0;
3
7
  min-height: 100vh;
package/app/index.ts CHANGED
@@ -1,3 +1,7 @@
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
+
1
5
  /**
2
6
  * 控制台前端宿主应用入口(scripts/client.ts 总装构建的主入口)。
3
7
  *
@@ -27,6 +31,12 @@ root.start();
27
31
 
28
32
  if (!global.static) {
29
33
  // global.static 为真表示纯静态构建(无后端),跳过连接
30
- const endpoint = new URL(global.endpoint, location.origin).toString();
31
- connect(root, () => new WebSocket(endpoint.replace(/^http/, "ws")));
34
+ const endpoint = new URL(
35
+ global.endpoint,
36
+ location.origin,
37
+ ).toString();
38
+ connect(
39
+ root,
40
+ () => new WebSocket(endpoint.replace(/^http/, "ws")),
41
+ );
32
42
  }
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  布局顶栏:左侧标题(默认取当前路由名)、右侧菜单区,
3
7
  移动端额外提供侧栏开关按钮与溢出菜单("...")按钮。
@@ -34,7 +38,10 @@
34
38
  </template>
35
39
 
36
40
  <script lang="ts" setup>
37
- import { type ActionContext, useMenu } from "@koishi-ce/client";
41
+ import {
42
+ type ActionContext,
43
+ useMenu,
44
+ } from "@koishi-ce/client";
38
45
  import { useRoute } from "vue-router";
39
46
 
40
47
  const props = defineProps<{
@@ -46,9 +53,14 @@ const props = defineProps<{
46
53
 
47
54
  // 溢出菜单按钮的点击触发器:打开 menuKey 对应的上下文菜单
48
55
  // (menuKey 为运行期字符串,这里断言为菜单键类型以复用字面量重载)
49
- const trigger = useMenu(props.menuKey as keyof ActionContext);
50
-
51
- defineEmits(["update:isLeftAsideOpen", "update:isRightAsideOpen"]);
56
+ const trigger = useMenu(
57
+ props.menuKey as keyof ActionContext,
58
+ );
59
+
60
+ defineEmits([
61
+ "update:isLeftAsideOpen",
62
+ "update:isRightAsideOpen",
63
+ ]);
52
64
 
53
65
  const route = useRoute();
54
66
  </script>
@@ -1,3 +1,7 @@
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
+
1
5
  /**
2
6
  * 布局插件:向全局 "layout" 插槽注册三栏布局组件。
3
7
  * order 取极小值保证它先于其它同插槽内容排序(作为页面骨架)。
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  三栏布局组件(页面模板中 <k-layout> 的内置实现):
3
7
  可选的左右侧栏 + 顶部 header + 主区域。
@@ -50,7 +54,10 @@
50
54
  </template>
51
55
 
52
56
  <script lang="ts" setup>
53
- import { type LegacyMenuItem, useContext } from "@koishi-ce/client";
57
+ import {
58
+ type LegacyMenuItem,
59
+ useContext,
60
+ } from "@koishi-ce/client";
54
61
  import { computed, ref, useSlots } from "vue";
55
62
  import { useRoute } from "vue-router";
56
63
  import LayoutHeader from "./header.vue";
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  顶栏菜单按钮(图标型):hidden/disabled/label/icon 均支持 getter,
3
7
  点击时以 action scope 为参调用对应 action。
@@ -50,7 +54,9 @@ const scope = computed(() =>
50
54
  function toValue<T>(getter: MaybeGetter<T>): T {
51
55
  if (typeof getter !== "function") return getter;
52
56
  // 收窄到 MaybeGetter 的函数分支(以作用域为参的 getter)
53
- return (getter as (current: typeof scope.value) => T)(scope.value);
57
+ return (getter as (current: typeof scope.value) => T)(
58
+ scope.value,
59
+ );
54
60
  }
55
61
 
56
62
  function trigger() {
@@ -1,3 +1,7 @@
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
+
1
5
  /**
2
6
  * 设置插件:
3
7
  * - 注册用户设置页(/settings/:name*,活动栏底部);
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  用户设置页:左侧 el-tree 分组导航 + 右侧当前分组的内容。
3
7
  分组内的条目三种形态二选一渲染:组件型(component)、表单型(schema),
@@ -52,10 +56,12 @@ interface Tree {
52
56
  }
53
57
 
54
58
  const data = computed(() =>
55
- Object.entries(ctx.internal.settings).map<Tree>(([id, [{ title }]]) => ({
56
- id,
57
- label: title,
58
- })),
59
+ Object.entries(ctx.internal.settings).map<Tree>(
60
+ ([id, [{ title }]]) => ({
61
+ id,
62
+ label: title,
63
+ }),
64
+ ),
59
65
  );
60
66
 
61
67
  function handleClick(tree: Tree) {
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  主题选择器:role 为 "theme" 的字符串 schema 的专用渲染组件。
3
7
  下拉选项按明 / 暗模式过滤(主题 id 以 -light / -dark 结尾),
@@ -31,7 +35,10 @@
31
35
 
32
36
  <script setup lang="ts">
33
37
  import { useContext, useI18nText } from "@koishi-ce/client";
34
- import { type Schema, SchemaBase } from "@koishi-ce/components";
38
+ import {
39
+ type Schema,
40
+ SchemaBase,
41
+ } from "@koishi-ce/components";
35
42
  import { computed, type PropType } from "vue";
36
43
 
37
44
  defineProps({
package/app/shims.d.ts CHANGED
@@ -1 +1,4 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ // Copyright (c) 2026-present Koishi-CE contributors.
3
+
1
4
  /// <reference path="../client/shims.d.ts" />
@@ -1,3 +1,7 @@
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
+
1
5
  /**
2
6
  * 状态栏插件:向全局 "status" 插槽注册状态栏主体(可悬停出 tooltip 的
3
7
  * 状态项容器),并向 "status-right" 插槽注册页面组件加载进度条。
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  页面组件按需加载进度条:按当前路由所需扩展(ctx.$loader.extensions)
3
7
  的完成比例显示进度;store.entry 尚未就绪时以不确定进度(indeterminate)展示。
@@ -18,6 +22,9 @@ const ctx = useContext();
18
22
 
19
23
  const progress = computed(() => {
20
24
  const states = Object.values(ctx.$loader.extensions);
21
- return states.filter((state) => state.done.value).length / states.length;
25
+ return (
26
+ states.filter((state) => state.done.value).length /
27
+ states.length
28
+ );
22
29
  });
23
30
  </script>
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  通用状态项容器(页面模板中 <k-status> 的内置实现):
3
7
  内容区 + 悬停时的 tooltip(内容经 el-scrollbar 限高滚动)。
@@ -1,3 +1,7 @@
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
+
1
5
  :root {
2
6
  --el-text-color-regular: var(--fg1);
3
7
  --el-text-color-primary: var(--fg2);
@@ -1,3 +1,7 @@
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
+
1
5
  :root, .theme-root.light {
2
6
  &[theme=hc-light] {
3
7
  --bg0: white;
@@ -1,3 +1,7 @@
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
+
1
5
  @use 'sass:color';
2
6
  @use './layout.scss';
3
7
  @use './element.scss';
@@ -1,3 +1,7 @@
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
+
1
5
  /**
2
6
  * 全局样式插件:引入 app 级 SCSS,并注册四套内置主题
3
7
  * (默认 / 高对比 × 明亮 / 暗色),供主题选择器列出与切换。
@@ -1,3 +1,7 @@
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
+
1
5
  :root {
2
6
  --aside-width: 17.5rem;
3
7
  --activity-width: 4rem;
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  活动栏单个图标按钮,也是拖拽排序的拖拽源:
3
7
  拖起时向 dataTransfer 写入 "activity:<id>" 文本供落点组件识别。
@@ -29,7 +33,10 @@ const isDragging = ref(false);
29
33
  function handleDragStart(event: DragEvent) {
30
34
  isDragging.value = true;
31
35
  // 约定的拖拽协议:以 "activity:" 前缀 + 活动 id 标识被拖动的活动项
32
- event.dataTransfer.setData("text/plain", `activity:${props.data.id}`);
36
+ event.dataTransfer.setData(
37
+ "text/plain",
38
+ `activity:${props.data.id}`,
39
+ );
33
40
  }
34
41
 
35
42
  function handleDragEnd(event: DragEvent) {
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  活动栏(最左侧图标栏):分 top / bottom 两区渲染活动项,
3
7
  项与项之间的分隔槽(activity-separator)同时充当拖拽排序的落点。
@@ -68,7 +72,9 @@ const groups = computed(() => {
68
72
  delete available[id];
69
73
  }
70
74
  }
71
- const list = Object.values(available).sort(([a], [b]) => a.order - b.order);
75
+ const list = Object.values(available).sort(
76
+ ([a], [b]) => a.order - b.order,
77
+ );
72
78
  // 放不下时:从头部取出溢出项折叠成一个组,top 位优先、同位再按 order 排,
73
79
  // 并以 "..." 图标作为该组的展示入口
74
80
  if (list.length * unit > total) {
@@ -82,11 +88,17 @@ const groups = computed(() => {
82
88
  return scale;
83
89
  })
84
90
  .flat();
85
- hidden.unshift({ icon: "activity:ellipsis" } as Activity);
91
+ hidden.unshift({
92
+ icon: "activity:ellipsis",
93
+ } as Activity);
86
94
  }
87
95
  // top 区按 order 逆序输出(order 大者靠上),bottom 区保持正序
88
- const top = list.filter(([data]) => data.position !== "bottom").reverse();
89
- const bottom = list.filter(([data]) => data.position === "bottom");
96
+ const top = list
97
+ .filter(([data]) => data.position !== "bottom")
98
+ .reverse();
99
+ const bottom = list.filter(
100
+ ([data]) => data.position === "bottom",
101
+ );
90
102
  return { top, bottom, hidden };
91
103
  });
92
104
 
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  活动栏条目:children[0] 为组的主图标,其余为并入该组的子活动,
3
7
  悬停 tooltip 中列出组内成员。条目本身也是拖拽落点——
@@ -34,7 +38,11 @@
34
38
  </template>
35
39
 
36
40
  <script lang="ts" setup>
37
- import { type Activity, useConfig, useMenu } from "@koishi-ce/client";
41
+ import {
42
+ type Activity,
43
+ useConfig,
44
+ useMenu,
45
+ } from "@koishi-ce/client";
38
46
  import type { Placement } from "element-plus";
39
47
  import { computed, ref, watch } from "vue";
40
48
  import { useRoute } from "vue-router";
@@ -80,13 +88,23 @@ const config = useConfig();
80
88
 
81
89
  // 原型链保留键:这类键在普通对象上会触发原型链存取器,禁止作为配置键读写
82
90
  // (id 来自拖拽事件的 dataTransfer 文本,属外部输入)
83
- const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
91
+ const UNSAFE_KEYS = new Set([
92
+ "__proto__",
93
+ "constructor",
94
+ "prototype",
95
+ ]);
84
96
 
85
97
  /** 取某活动的覆盖配置(不存在则创建);保留键返回一次性空对象,防原型污染。
86
98
  * 守卫须用显式字符串比较(Set.has 形式 CodeQL 无法识别为阻断) */
87
- function ensureOverride(id: string): Record<string, unknown> {
99
+ function ensureOverride(
100
+ id: string,
101
+ ): Record<string, unknown> {
88
102
  const activities = (config.value.activities ??= {});
89
- if (id === "__proto__" || id === "constructor" || id === "prototype") {
103
+ if (
104
+ id === "__proto__" ||
105
+ id === "constructor" ||
106
+ id === "prototype"
107
+ ) {
90
108
  return {};
91
109
  }
92
110
  return (activities[id] ??= {});
@@ -1,3 +1,7 @@
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
+
1
5
  <!--
2
6
  活动栏条目间的分隔槽:平时不可见,拖拽悬停时显示横线提示,
3
7
  松手即把被拖项移动到该位置(拖拽排序的核心落点组件)。
@@ -14,7 +18,11 @@
14
18
  </template>
15
19
 
16
20
  <script lang="ts" setup>
17
- import { type Activity, useConfig, useContext } from "@koishi-ce/client";
21
+ import {
22
+ type Activity,
23
+ useConfig,
24
+ useContext,
25
+ } from "@koishi-ce/client";
18
26
  import { type ComputedRef, inject, ref } from "vue";
19
27
 
20
28
  type Position = "top" | "bottom";
@@ -24,7 +32,9 @@ const props = defineProps<{
24
32
  position: Position;
25
33
  }>();
26
34
 
27
- const groups = inject("groups") as ComputedRef<Record<Position, Activity[][]>>;
35
+ const groups = inject("groups") as ComputedRef<
36
+ Record<Position, Activity[][]>
37
+ >;
28
38
 
29
39
  const hasDragOver = ref(false);
30
40
 
@@ -41,13 +51,23 @@ const ctx = useContext();
41
51
 
42
52
  // 原型链保留键:这类键在普通对象上会触发原型链存取器,禁止作为配置键读写
43
53
  // (id 来自拖拽事件的 dataTransfer 文本,属外部输入)
44
- const UNSAFE_KEYS = new Set(["__proto__", "constructor", "prototype"]);
54
+ const UNSAFE_KEYS = new Set([
55
+ "__proto__",
56
+ "constructor",
57
+ "prototype",
58
+ ]);
45
59
 
46
60
  /** 取某活动的覆盖配置(不存在则创建);保留键返回一次性空对象,防原型污染。
47
61
  * 守卫须用显式字符串比较(Set.has 形式 CodeQL 无法识别为阻断) */
48
- function ensureOverride(id: string): Record<string, unknown> {
62
+ function ensureOverride(
63
+ id: string,
64
+ ): Record<string, unknown> {
49
65
  const activities = (config.value.activities ??= {});
50
- if (id === "__proto__" || id === "constructor" || id === "prototype") {
66
+ if (
67
+ id === "__proto__" ||
68
+ id === "constructor" ||
69
+ id === "prototype"
70
+ ) {
51
71
  return {};
52
72
  }
53
73
  return (activities[id] ??= {});
@@ -59,7 +79,9 @@ function handleDrop(event: DragEvent) {
59
79
  // 只响应活动栏自身的拖拽协议
60
80
  if (!text.startsWith("activity:")) return;
61
81
  const id = text.slice(9);
62
- const list = groups.value[props.position].map(([item]) => item);
82
+ const list = groups.value[props.position].map(
83
+ ([item]) => item,
84
+ );
63
85
  const oldIndex = list.findIndex((item) => item.id === id);
64
86
  // 落点即原位(含紧邻原位的前一格)时无需移动
65
87
  if (
@@ -95,10 +117,12 @@ function handleDrop(event: DragEvent) {
95
117
  // 介于两锚点之间的项按线性插值重算 order;
96
118
  // 只有一侧锚点时按步长 100 单向递增 / 递减;两侧都没有则恢复默认
97
119
  const anchorL = list.findLastIndex(
98
- (item, i) => i < index && item.order === item.options.order,
120
+ (item, i) =>
121
+ i < index && item.order === item.options.order,
99
122
  );
100
123
  const anchorR = list.findIndex(
101
- (item, i) => i > index && item.order === item.options.order,
124
+ (item, i) =>
125
+ i > index && item.order === item.options.order,
102
126
  );
103
127
  if (anchorL === -1) {
104
128
  if (anchorR === -1) {
@@ -113,24 +137,36 @@ function handleDrop(event: DragEvent) {
113
137
  } else {
114
138
  if (anchorR === -1) {
115
139
  let order = list[anchorL].options.order;
116
- for (let index = anchorL + 1; index < list.length; index++) {
140
+ for (
141
+ let index = anchorL + 1;
142
+ index < list.length;
143
+ index++
144
+ ) {
117
145
  const override = ensureOverride(list[index].id);
118
146
  override.order = order -= 100;
119
147
  }
120
148
  } else {
121
149
  let orderL = list[anchorL].options.order;
122
150
  let orderR = list[anchorR].options.order;
123
- for (let index = anchorL + 1; index < anchorR; index++) {
151
+ for (
152
+ let index = anchorL + 1;
153
+ index < anchorR;
154
+ index++
155
+ ) {
124
156
  const override = ensureOverride(list[index].id);
125
157
  override.order =
126
158
  orderL +
127
- ((orderR - orderL) * (index - anchorL)) / (anchorR - anchorL);
159
+ ((orderR - orderL) * (index - anchorL)) /
160
+ (anchorR - anchorL);
128
161
  }
129
162
  }
130
163
  }
131
164
 
132
165
  // 覆盖配置为空对象时删除该键,避免残留无意义的配置项
133
- if (!Object.keys(override).length && !UNSAFE_KEYS.has(id)) {
166
+ if (
167
+ !Object.keys(override).length &&
168
+ !UNSAFE_KEYS.has(id)
169
+ ) {
134
170
  delete config.value.activities[id];
135
171
  }
136
172
  }
@@ -1,3 +1,7 @@
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
+
1
5
  /**
2
6
  * 活动栏(activity bar)相关的类型扩充:
3
7
  * - 为 ActionContext 补充 "theme.activity" 菜单的求值上下文类型;
@@ -1,3 +1,7 @@
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
+
1
5
  <!-- 404 空白页:控制台本体加载成功但没有任何扩展页面时的兜底展示 -->
2
6
  <template>
3
7
  <div class="blank">