@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
@@ -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
  <!-- 实心五角星图标(注册名 "star-full"),用于已收藏/已订阅状态 -->
2
6
  <template>
3
7
  <svg class="k-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512">
@@ -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
  <!-- 播放(三角)图标(注册名 "start"),用于启动/运行 -->
2
6
  <template>
3
7
  <svg class="k-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512">
@@ -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
  <!-- 标签图标(注册名 "tag"),用于标签/标记 -->
2
6
  <template>
3
7
  <svg class="k-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
@@ -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
  <!-- 实心圆形叉号图标(注册名 "times-full"),用于失败/关闭状态 -->
2
6
  <template>
3
7
  <svg class="k-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
@@ -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
  <!-- 扳手与螺丝刀工具图标(注册名 "tools"),用于维护/调试功能 -->
2
6
  <template>
3
7
  <svg class="k-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
@@ -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
  <!-- 逆时针旋转箭头图标(注册名 "undo"),用于向左旋转/撤销 -->
2
6
  <template>
3
7
  <svg class="k-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
@@ -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
  <!-- 用户人形图标(注册名 "user"),用于用户/登录相关界面 -->
2
6
  <template>
3
7
  <svg class="k-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
@@ -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
  *
@@ -11,10 +15,16 @@
11
15
  // 无法解析(无 paths),故直接相对导入工作区内该包的浏览器端入口,
12
16
  // 与构建器 collectWorkspaceAliases 的映射目标一致
13
17
 
14
- import Element, { ElLoading, ElMessage, ElMessageBox } from "element-plus";
18
+ import Element, {
19
+ ElLoading,
20
+ ElMessage,
21
+ ElMessageBox,
22
+ } from "element-plus";
15
23
  import Markdown from "marked-vue";
16
24
  import type { App } from "vue";
17
- import components, { SchemaBase } from "../../../components/client/index.ts";
25
+ import components, {
26
+ SchemaBase,
27
+ } from "../../../components/client/index.ts";
18
28
 
19
29
  import { store } from "../data";
20
30
  import ChatImage from "./chat/image.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
  k-card:通用卡片容器。
3
7
  可选 header(标题文字或 header 插槽)与 footer 插槽;
@@ -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-content:页面主内容区容器。
3
7
  水平居中、限宽 55rem,自带垂直滚动(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
  <!--
2
6
  k-empty:空状态占位组件。
3
7
  居中展示 Koishi 徽标图标与默认插槽文案,用于无数据 / 无权限等场景。
@@ -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
  import type { App } from "vue";
2
6
  import Card from "./card.vue";
3
7
  import Content from "./content.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
  k-tab-group:侧栏式选项卡分组。
3
7
  遍历 data(键 → 条目对象),filter 可按条目过滤显示;
@@ -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-tab-item:选项卡条目(配合 k-tab-group 使用)。
3
7
  label 为条目标识兼默认文案,点击时 emit update:modelValue;
@@ -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
  import { type App, defineComponent, h } from "vue";
2
6
  import { RouterLink } from "vue-router";
3
7
  import { useContext } from "../context";
@@ -17,7 +21,9 @@ const KActivityLink = defineComponent({
17
21
  setup(props, { slots }) {
18
22
  const ctx = useContext();
19
23
  return () => {
20
- const activity = props.id ? ctx.$router.pages[props.id] : undefined;
24
+ const activity = props.id
25
+ ? ctx.$router.pages[props.id]
26
+ : undefined;
21
27
  return h(
22
28
  RouterLink,
23
29
  {
@@ -26,7 +32,8 @@ const KActivityLink = defineComponent({
26
32
  activity?.path.replace(/:.+/, "")) as string,
27
33
  },
28
34
  {
29
- default: () => slots["default"]?.() ?? activity?.name,
35
+ default: () =>
36
+ slots["default"]?.() ?? activity?.name,
30
37
  },
31
38
  );
32
39
  };
@@ -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
  perms.vue:权限选择控件(array + perms 角色)。
3
7
  把服务端下发的扁平权限名(如 "channel.xxx")按 ":" 逐级拆分,
@@ -28,7 +32,10 @@
28
32
  // schemastery-vue 链接(它是 components 的依赖,Bun 隔离布局下不跨包可见)
29
33
 
30
34
  import { store } from "@koishi-ce/client";
31
- import { type Schema, SchemaBase } from "@koishi-ce/components";
35
+ import {
36
+ type Schema,
37
+ SchemaBase,
38
+ } from "@koishi-ce/components";
32
39
  import type { CascaderOption } from "element-plus";
33
40
  import { computed, type PropType } from "vue";
34
41
 
@@ -49,15 +56,29 @@ const config = SchemaBase.useModel();
49
56
  * 逐段查找/创建级联节点;中间段(还有下级时)标记为 disabled,
50
57
  * 因为 el-cascader 的父节点勾选会影响子级,而中间段并非真实权限。
51
58
  */
52
- function addNode(nodes: CascaderOption[], path: string[], prefix = "") {
59
+ function addNode(
60
+ nodes: CascaderOption[],
61
+ path: string[],
62
+ prefix = "",
63
+ ) {
53
64
  const name = path.shift();
54
- let node = nodes.find((node) => node.value === prefix + name);
65
+ let node = nodes.find(
66
+ (node) => node.value === prefix + name,
67
+ );
55
68
  if (!node) {
56
- node = { value: prefix + name, label: name, disabled: !!path.length };
69
+ node = {
70
+ value: prefix + name,
71
+ label: name,
72
+ disabled: !!path.length,
73
+ };
57
74
  nodes.push(node);
58
75
  }
59
76
  if (!path.length) return;
60
- addNode((node.children ||= []), path, `${prefix + name}:`);
77
+ addNode(
78
+ (node.children ||= []),
79
+ path,
80
+ `${prefix + name}:`,
81
+ );
61
82
  }
62
83
 
63
84
  // 由全部权限名构建级联选项树
@@ -1,4 +1,13 @@
1
- import { type App, type Component, defineComponent, h } from "vue";
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
+ import {
6
+ type App,
7
+ type Component,
8
+ defineComponent,
9
+ h,
10
+ } from "vue";
2
11
  import { useContext } from "../context";
3
12
 
4
13
  /** 插槽条目的基础形状:参与排序的组件 */
@@ -43,10 +52,15 @@ export const KSlot = defineComponent({
43
52
  ? []
44
53
  : [...(slots["default"]?.() || [])]
45
54
  .filter((node) => node.type === KSlotItem)
46
- .map((node) => ({ node, order: node.props?.["order"] || 0 }));
55
+ .map((node) => ({
56
+ node,
57
+ order: node.props?.["order"] || 0,
58
+ }));
47
59
  // external:ctx.slot() 注册的视图,disabled 的不渲染
48
60
  const external = [
49
- ...(props.name ? ctx.$router.views[props.name] || [] : []),
61
+ ...(props.name
62
+ ? ctx.$router.views[props.name] || []
63
+ : []),
50
64
  ]
51
65
  .filter((item) => !item.disabled?.())
52
66
  .map((item) => ({
@@ -81,7 +95,12 @@ function defineSlotComponent(name: string) {
81
95
  return defineComponent({
82
96
  inheritAttrs: false,
83
97
  setup(_, { slots, attrs }) {
84
- return () => h(KSlot, { name, data: attrs, single: true }, slots);
98
+ return () =>
99
+ h(
100
+ KSlot,
101
+ { name, data: attrs, single: true },
102
+ slots,
103
+ );
85
104
  },
86
105
  });
87
106
  }
package/client/context.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
  import * as cordis from "cordis";
2
6
  import {
3
7
  type App,
@@ -22,7 +26,8 @@ import ThemeService from "./plugins/theme";
22
26
  // —— 布局相关的 Context 类型扩展 ——
23
27
 
24
28
  /** 前端侧事件表:在 cordis 事件的基础上扩展本库自定义事件 */
25
- export interface Events<C extends Context = Context> extends cordis.Events<C> {}
29
+ export interface Events<C extends Context = Context>
30
+ extends cordis.Events<C> {}
26
31
 
27
32
  /**
28
33
  * 前端 Context 接口:各服务插件通过 `declare module "../context"`
@@ -80,7 +85,10 @@ export class Context extends cordis.Context {
80
85
  this.app = createApp(
81
86
  defineComponent({
82
87
  setup: () => () => [
83
- h(resolveComponent("k-slot"), { name: "root", single: true }),
88
+ h(resolveComponent("k-slot"), {
89
+ name: "root",
90
+ single: true,
91
+ }),
84
92
  h(resolveComponent("k-slot"), { name: "global" }),
85
93
  ],
86
94
  }),
@@ -111,12 +119,16 @@ export class Context extends cordis.Context {
111
119
  */
112
120
  addEventListener<K extends keyof WindowEventMap>(
113
121
  type: K,
114
- listener: (this: Window, ev: WindowEventMap[K]) => unknown,
122
+ listener: (
123
+ this: Window,
124
+ ev: WindowEventMap[K],
125
+ ) => unknown,
115
126
  options?: boolean | AddEventListenerOptions,
116
127
  ) {
117
128
  return this.effect(() => {
118
129
  window.addEventListener(type, listener, options);
119
- return () => window.removeEventListener(type, listener, options);
130
+ return () =>
131
+ window.removeEventListener(type, listener, options);
120
132
  });
121
133
  }
122
134
 
package/client/data.ts CHANGED
@@ -1,5 +1,12 @@
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
  /// <reference path="./shims.d.ts" />
2
- import type { Promisify, Universal } from "@koishi-ce/koishi";
6
+ import type {
7
+ Promisify,
8
+ Universal,
9
+ } from "@koishi-ce/koishi";
3
10
  import type {
4
11
  ClientConfig,
5
12
  Console,
@@ -39,7 +46,8 @@ export function withProxy(url: string) {
39
46
  /** 当前与服务端的 WebSocket 连接(未连接时为 null) */
40
47
  export const socket = ref<Universal.WebSocket | null>(null);
41
48
  /** 按事件名登记的推送监听器(receive 注册) */
42
- const listeners: Record<string, (data: unknown) => void> = {};
49
+ const listeners: Record<string, (data: unknown) => void> =
50
+ {};
43
51
  /** 按报文 id 暂存的 RPC 应答钩子 [resolve, reject](send 写入) */
44
52
  const responseHooks: Record<
45
53
  string,
@@ -89,19 +97,26 @@ type StoreValue = Store[keyof Store];
89
97
 
90
98
  // 服务端整表推送:直接覆盖 store 中对应键
91
99
  // (key 为联合类型时直写 store[key] 会被要求交叉类型,故经 Record 视图写入)
92
- receive<{ key: keyof Store; value: unknown }>("data", ({ key, value }) => {
93
- (store as Record<keyof Store, StoreValue>)[key] = value as StoreValue;
94
- });
100
+ receive<{ key: keyof Store; value: unknown }>(
101
+ "data",
102
+ ({ key, value }) => {
103
+ (store as Record<keyof Store, StoreValue>)[key] =
104
+ value as StoreValue;
105
+ },
106
+ );
95
107
 
96
108
  // 服务端增量推送:数组做追加,对象做浅合并
97
- receive<{ key: keyof Store; value: unknown }>("patch", ({ key, value }) => {
98
- const current = store[key];
99
- if (Array.isArray(current)) {
100
- (current as unknown[]).push(...(value as unknown[]));
101
- } else if (current) {
102
- Object.assign(current, value);
103
- }
104
- });
109
+ receive<{ key: keyof Store; value: unknown }>(
110
+ "patch",
111
+ ({ key, value }) => {
112
+ const current = store[key];
113
+ if (Array.isArray(current)) {
114
+ (current as unknown[]).push(...(value as unknown[]));
115
+ } else if (current) {
116
+ Object.assign(current, value);
117
+ }
118
+ },
119
+ );
105
120
 
106
121
  // RPC 应答分发:按报文 id 找到 send() 留下的 resolve/reject 并结算
107
122
  receive<{ id: string; value?: unknown; error?: unknown }>(
@@ -127,7 +142,10 @@ receive<{ id: string; value?: unknown; error?: unknown }>(
127
142
  * @param ctx 根 Context,用于把消息转发为 cordis 事件
128
143
  * @param callback 创建 WebSocket 实例的工厂函数
129
144
  */
130
- export function connect(ctx: Context, callback: () => Universal.WebSocket) {
145
+ export function connect(
146
+ ctx: Context,
147
+ callback: () => Universal.WebSocket,
148
+ ) {
131
149
  const value = callback();
132
150
 
133
151
  let sendTimer: number;
@@ -138,8 +156,14 @@ export function connect(ctx: Context, callback: () => Universal.WebSocket) {
138
156
  if (!global.heartbeat) return;
139
157
  clearTimeout(sendTimer);
140
158
  clearTimeout(closeTimer);
141
- sendTimer = +setTimeout(() => send("ping"), global.heartbeat.interval);
142
- closeTimer = +setTimeout(() => value?.close(), global.heartbeat.timeout);
159
+ sendTimer = +setTimeout(
160
+ () => send("ping"),
161
+ global.heartbeat.interval,
162
+ );
163
+ closeTimer = +setTimeout(
164
+ () => value?.close(),
165
+ global.heartbeat.timeout,
166
+ );
143
167
  };
144
168
 
145
169
  const reconnect = () => {
@@ -148,10 +172,14 @@ export function connect(ctx: Context, callback: () => Universal.WebSocket) {
148
172
  for (const key in store) {
149
173
  (store as Record<string, unknown>)[key] = undefined;
150
174
  }
151
- console.log("[koishi] websocket disconnected, will retry in 1s...");
175
+ console.log(
176
+ "[koishi] websocket disconnected, will retry in 1s...",
177
+ );
152
178
  setTimeout(() => {
153
179
  connect(ctx, callback).then(location.reload, () => {
154
- console.log("[koishi] websocket disconnected, will retry in 1s...");
180
+ console.log(
181
+ "[koishi] websocket disconnected, will retry in 1s...",
182
+ );
155
183
  });
156
184
  }, 1000);
157
185
  };
@@ -159,7 +187,12 @@ export function connect(ctx: Context, callback: () => Universal.WebSocket) {
159
187
  value.addEventListener("message", (ev) => {
160
188
  refresh();
161
189
  const data = JSON.parse(ev.data);
162
- console.debug("↓%c", "color:purple", data.type, data.body);
190
+ console.debug(
191
+ "↓%c",
192
+ "color:purple",
193
+ data.type,
194
+ data.body,
195
+ );
163
196
  if (data.type in listeners) {
164
197
  listeners[data.type]?.(data.body);
165
198
  }
@@ -168,11 +201,13 @@ export function connect(ctx: Context, callback: () => Universal.WebSocket) {
168
201
 
169
202
  value.addEventListener("close", reconnect);
170
203
 
171
- return new Promise<Universal.WebSocket.Event>((resolve, reject) => {
172
- value.addEventListener("open", (event) => {
173
- socket.value = markRaw(value);
174
- resolve(event);
175
- });
176
- value.addEventListener("error", reject);
177
- });
204
+ return new Promise<Universal.WebSocket.Event>(
205
+ (resolve, reject) => {
206
+ value.addEventListener("open", (event) => {
207
+ socket.value = markRaw(value);
208
+ resolve(event);
209
+ });
210
+ value.addEventListener("error", reject);
211
+ },
212
+ );
178
213
  }
package/client/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
  * @koishi_ce/client 包的主入口(Koishi 控制台前端组件库)。
3
7
  *
@@ -33,7 +37,8 @@ export const ScopeStatus = {
33
37
  DISPOSED: 4,
34
38
  } as const;
35
39
 
36
- export type ScopeStatus = (typeof ScopeStatus)[keyof typeof ScopeStatus];
40
+ export type ScopeStatus =
41
+ (typeof ScopeStatus)[keyof typeof ScopeStatus];
37
42
  export * from "./components";
38
43
  export * from "./context";
39
44
  export * from "./data";
@@ -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
  * 动作(action)与菜单服务:控制台的快捷键、右键菜单、
3
7
  * 作用域变量的统一管理。
@@ -7,7 +11,11 @@
7
11
  * 绑定右键菜单。作用域变量支持 "a.b" 点分键,经 Flatten 类型
8
12
  * 与运行时代理呈现为嵌套对象。
9
13
  */
10
- import { type Dict, type Intersect, remove } from "cosmokit";
14
+ import {
15
+ type Dict,
16
+ type Intersect,
17
+ remove,
18
+ } from "cosmokit";
11
19
  import {
12
20
  type MaybeRefOrGetter,
13
21
  markRaw,
@@ -54,7 +62,8 @@ export interface ActionOptions {
54
62
  }
55
63
 
56
64
  /** 旧版菜单项写法(字段与 ActionOptions 部分重叠),仅为兼容保留 */
57
- export type LegacyMenuItem = Partial<ActionOptions> & Omit<MenuItem, "id">;
65
+ export type LegacyMenuItem = Partial<ActionOptions> &
66
+ Omit<MenuItem, "id">;
58
67
 
59
68
  /** 菜单项:label / type / icon 均支持写成随作用域变化的 getter */
60
69
  export interface MenuItem {
@@ -66,10 +75,14 @@ export interface MenuItem {
66
75
  }
67
76
 
68
77
  /** 值或(以作用域为参数的)取值函数 */
69
- export type MaybeGetter<T> = T | ((scope: Flatten<ActionContext>) => T);
78
+ export type MaybeGetter<T> =
79
+ | T
80
+ | ((scope: Flatten<ActionContext>) => T);
70
81
 
71
82
  /** 作用域存储:扁平键到「值或 ref 或 getter」的映射 */
72
- type Store<S extends {}> = { [K in keyof S]?: MaybeRefOrGetter<S[K]> };
83
+ type Store<S extends {}> = {
84
+ [K in keyof S]?: MaybeRefOrGetter<S[K]>;
85
+ };
73
86
 
74
87
  /**
75
88
  * 把 "a.b.c" 形式的扁平键名归并为 { a: { b: { c } } } 的嵌套结构,
@@ -99,9 +112,14 @@ export interface ActiveMenu {
99
112
  * 触发时把附加值写入作用域,并在鼠标位置弹出自定义菜单(由 global 插槽渲染)。
100
113
  * @param id 菜单标识,menu() 注册的条目将展示在该菜单中
101
114
  */
102
- export function useMenu<K extends keyof ActionContext>(id: K) {
115
+ export function useMenu<K extends keyof ActionContext>(
116
+ id: K,
117
+ ) {
103
118
  const ctx = useContext();
104
- return (event: MouseEvent, value: MaybeRefOrGetter<ActionContext[K]>) => {
119
+ return (
120
+ event: MouseEvent,
121
+ value: MaybeRefOrGetter<ActionContext[K]>,
122
+ ) => {
105
123
  ctx.define(id, value);
106
124
  event.preventDefault();
107
125
  const { clientX, clientY } = event;
@@ -129,7 +147,9 @@ export default class ActionService extends Service {
129
147
  super(ctx, "$action", true);
130
148
  ctx.mixin("$action", ["action", "menu", "define"]);
131
149
 
132
- ctx.internal.scope = shallowReactive({} as Store<ActionContext>);
150
+ ctx.internal.scope = shallowReactive(
151
+ {} as Store<ActionContext>,
152
+ );
133
153
  ctx.internal.menus = reactive({});
134
154
  ctx.internal.actions = reactive({});
135
155
  ctx.internal.activeMenus = reactive([]);
@@ -138,7 +158,9 @@ export default class ActionService extends Service {
138
158
  // 与键盘事件逐项比对(ctrl 在 macOS 平台映射为 meta 键)
139
159
  ctx.addEventListener("keydown", (event) => {
140
160
  const scope = this.createScope();
141
- for (const action of Object.values(ctx.internal.actions)) {
161
+ for (const action of Object.values(
162
+ ctx.internal.actions,
163
+ )) {
142
164
  if (!action.shortcut) continue;
143
165
  const keys = action.shortcut
144
166
  .split("+")
@@ -154,7 +176,11 @@ export default class ActionService extends Service {
154
176
  continue;
155
177
  case "ctrl":
156
178
  // macOS 没有 ctrl+字母 的快捷键惯例,改用 Cmd(meta)
157
- if (navigator.platform.toLowerCase().includes("mac")) {
179
+ if (
180
+ navigator.platform
181
+ .toLowerCase()
182
+ .includes("mac")
183
+ ) {
158
184
  metaKey = true;
159
185
  } else {
160
186
  ctrlKey = true;
@@ -191,7 +217,8 @@ export default class ActionService extends Service {
191
217
  items.forEach((item) => insert(list, item));
192
218
  return () => {
193
219
  items.forEach((item) => remove(list, item));
194
- if (!list.length) delete this.ctx.internal.menus[id];
220
+ if (!list.length)
221
+ delete this.ctx.internal.menus[id];
195
222
  };
196
223
  });
197
224
  }
@@ -218,7 +245,10 @@ export default class ActionService extends Service {
218
245
  * 可用 override 临时覆盖若干键值。
219
246
  */
220
247
  createScope(override = {}) {
221
- const scope = { ...this.ctx.internal.scope, ...override };
248
+ const scope = {
249
+ ...this.ctx.internal.scope,
250
+ ...override,
251
+ };
222
252
  return createScope(scope);
223
253
  }
224
254
  }
@@ -237,11 +267,18 @@ function createScope(
237
267
  if (typeof key === "symbol")
238
268
  return (target as Record<symbol, unknown>)[key];
239
269
  key = prefix + key;
240
- const source = scope as Record<string, MaybeRefOrGetter<unknown>>;
270
+ const source = scope as Record<
271
+ string,
272
+ MaybeRefOrGetter<unknown>
273
+ >;
241
274
  if (key in scope) return toValue(source[key]);
242
275
  // 键本身不存在,但存在以其为前缀的子键:返回子级作用域代理
243
276
  const _prefix = `${key}.`;
244
- if (Object.keys(scope).some((k) => k.startsWith(_prefix))) {
277
+ if (
278
+ Object.keys(scope).some((k) =>
279
+ k.startsWith(_prefix),
280
+ )
281
+ ) {
245
282
  return createScope(scope, `${key}.`);
246
283
  }
247
284
  // 完全未命中:显式返回 undefined(noImplicitReturns)
@@ -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
  import { watchEffect } from "vue";
2
6
  import { createI18n } from "vue-i18n";
3
7
  import type { Context } from "../context";