hiwork-core 0.0.29 → 0.0.35
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/cordis.patch.yml +15 -1
- package/lib/ai-router-client.js +394 -2
- package/lib/client-policy.js +2 -2
- package/lib/client.js +2483 -448
- package/lib/image-rpc.js +306 -0
- package/lib/imagegen.js +424 -0
- package/lib/index.js +19212 -7694
- package/lib/knowledge-config.js +91 -1
- package/lib/provision-session.js +10 -2
- package/lib/tool-names.js +10 -0
- package/lib/tools.js +205 -0
- package/lib/types/ai-router-client.d.ts +255 -2
- package/lib/types/client/about-section.d.ts +27 -7
- package/lib/types/client/controller.d.ts +17 -9
- package/lib/types/client/feature-center.d.ts +46 -1
- package/lib/types/client/feature-registry.d.ts +36 -3
- package/lib/types/client/image-cards.d.ts +218 -0
- package/lib/types/client/image-styles.d.ts +9 -0
- package/lib/types/client/images-data.d.ts +127 -0
- package/lib/types/client/images-page.d.ts +97 -0
- package/lib/types/client/index.d.ts +0 -1
- package/lib/types/client/login-gate.d.ts +2 -2
- package/lib/types/client/logo.d.ts +7 -2
- package/lib/types/client/menu-preferences.d.ts +163 -0
- package/lib/types/client/menu-section.d.ts +5 -0
- package/lib/types/client/menu-store.d.ts +101 -0
- package/lib/types/client/styles.d.ts +16 -0
- package/lib/types/client/types.d.ts +9 -0
- package/lib/types/client/voice/entry.d.ts +29 -3
- package/lib/types/image-rpc.d.ts +142 -0
- package/lib/types/imagegen.d.ts +241 -0
- package/lib/types/index.d.ts +70 -8
- package/lib/types/knowledge-config.d.ts +50 -2
- package/lib/types/tool-names.d.ts +11 -0
- package/lib/types/tools.d.ts +80 -0
- package/lib/types/user-agent.d.ts +31 -0
- package/lib/user-agent.js +54 -0
- package/lib/web-search.js +2 -2
- package/package.json +35 -25
- package/lib/types/client/daily-items.d.ts +0 -25
|
@@ -1,10 +1,30 @@
|
|
|
1
|
-
import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
1
|
+
import type { InjectFace, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
|
|
2
|
+
import type { AppVersionFace } from './app-version.ts';
|
|
2
3
|
/**
|
|
3
4
|
* 设置弹窗「关于」分区(自 hiwork-dsh-ui 迁移,注册为 settings.section 列表末位):
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* 品牌横标、标语、能力关键词、安全承诺,以及版本与官网信息。
|
|
6
|
+
* dsh-ui 的「配套管理插件」区块在其源码中已被 HIDE_DEPENDENCIES 隐藏且依赖其 host
|
|
7
|
+
* 端点,不迁移。横标只有深色资产,暗色主题复用 brandCss 对 .hiwork-brand-wordmark
|
|
8
|
+
* 的反白规则。
|
|
9
|
+
*
|
|
10
|
+
* 版本展示的历史(2026-09-25 回归):dsh-ui 时代「关于」里有一个点击复制的版本
|
|
11
|
+
* 胶囊,迁移时被移除——当时版本是 client 侧硬编码常量,桌面端每次发版都要手工
|
|
12
|
+
* 同步。后来侧栏 brand 行改走 `/hiwork-app` 频道动态取桌面端版本(app-version.ts),
|
|
13
|
+
* 手工同步问题不复存在,本分区重新展示版本也走同一频道、同一取数面,与 brand 行
|
|
14
|
+
* 单一来源;`HIWORK_VERSION` 常量只在纯 web(无桌面壳)时兜底。
|
|
15
|
+
*
|
|
16
|
+
* 官网地址从 {@link MANUAL_URL} 派生而不是再写一遍域名:manual-link.ts 是全仓
|
|
17
|
+
* 唯一的域名落点(见其注释),这里多写一份立刻变成两处能漂移的真相。
|
|
7
18
|
*/
|
|
8
|
-
export type AboutSectionProps = PropsRuntime<'settings.section'
|
|
9
|
-
|
|
10
|
-
|
|
19
|
+
export type AboutSectionProps = PropsRuntime<'settings.section'> & InjectFace<{
|
|
20
|
+
appVersion: AppVersionFace;
|
|
21
|
+
}>;
|
|
22
|
+
/** 点击复制版本信息时写入剪贴板的文本。 */
|
|
23
|
+
export declare function versionClipboardText(version: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* 复制文本到剪贴板(导出供单测驱动):剪贴板 API 不可用 / 写入被拒一律返回
|
|
26
|
+
* false,绝不抛异常——复制是锦上添花,失败只把提示切到「复制失败」。
|
|
27
|
+
*/
|
|
28
|
+
export declare function copyVersionText(text: string): Promise<boolean>;
|
|
29
|
+
/** HiWork 品牌区(标语)、版本、能力关键词、安全承诺与维护信息。 */
|
|
30
|
+
export declare function AboutSection({ appVersion }: AboutSectionProps): import("react").JSX.Element;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* AI Router 网关设置卡片的控制器:绑定 `ai-router`
|
|
3
|
-
*
|
|
2
|
+
* AI Router 网关设置卡片的控制器:绑定 `ai-router` 配置表单(= Host 插件的 profile
|
|
3
|
+
* entry id),把表单快照投影成卡片状态,并通过「写请求字段 + Host 监听」触发登录/刷新/退出。
|
|
4
4
|
* 命名空间与凭据 ref 在此拼写(dsh 惯例:client 不 import Host 代码)。
|
|
5
5
|
*/
|
|
6
6
|
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
7
|
-
import type {
|
|
7
|
+
import type { ConfigForm } from '@deepseek-ai/dsh-client-ui-settings/client';
|
|
8
|
+
import type { MenuPreferences } from './menu-preferences.ts';
|
|
8
9
|
import type { LoginError } from './types.ts';
|
|
9
|
-
/** ai-router
|
|
10
|
+
/** ai-router 配置命名空间:Host 半边 entry id(cordis.patch.yml 的 insert id)与此逐字一致。 */
|
|
10
11
|
export declare const AI_ROUTER_NS = "ai-router";
|
|
11
12
|
/**
|
|
12
13
|
* 设置分区与卡片标题共用文案:settings.section 注册的 label 也是弹窗导航按钮的
|
|
@@ -63,6 +64,8 @@ export interface AiRouterCardSettings {
|
|
|
63
64
|
authorizeUrl: string;
|
|
64
65
|
startedAt: string;
|
|
65
66
|
};
|
|
67
|
+
/** 侧栏菜单偏好(rc.2 起并入本 entry 的 `menu` volatile 字段,见 MenuStore)。 */
|
|
68
|
+
menu?: MenuPreferences;
|
|
66
69
|
}
|
|
67
70
|
/** 单个模型的额度视图(client 侧结构类型,不 import Host)。 */
|
|
68
71
|
export interface GatewayBudgetView {
|
|
@@ -171,16 +174,21 @@ export declare function openExternalUrl(url: string | undefined): void;
|
|
|
171
174
|
* @param url - Host 回写的授权页 URL;缺省 / 非法时为 no-op。
|
|
172
175
|
*/
|
|
173
176
|
export declare function openAuthorizeUrl(url: string | undefined): void;
|
|
177
|
+
/**
|
|
178
|
+
* 写失败口径:ConfigForm 的 set/unset/mutate 返回 `Promise<boolean>`(false = Host 拒绝,
|
|
179
|
+
* 表单控制器已自行回读镜像刷新状态)。rc.1 的 SettingsScope 同样是静默吞掉拒写,这里
|
|
180
|
+
* 保持同一语义——调用点检查返回值但不额外抛错,界面状态由快照刷新呈现。
|
|
181
|
+
*/
|
|
174
182
|
/** 请求 Host 发起浏览器授权登录(Host 生成 PKCE 后回写 `browserLogin`)。 */
|
|
175
|
-
export declare function requestBrowserLogin(
|
|
183
|
+
export declare function requestBrowserLogin(form: ConfigForm<AiRouterCardSettings>): Promise<void>;
|
|
176
184
|
/** 取消 / 结束浏览器登录等待态:清掉请求字段与 Host 回写的授权 URL。 */
|
|
177
|
-
export declare function clearBrowserLogin(
|
|
178
|
-
/** 桥接 `ai-router`
|
|
185
|
+
export declare function clearBrowserLogin(form: ConfigForm<AiRouterCardSettings>): Promise<void>;
|
|
186
|
+
/** 桥接 `ai-router` 配置表单到卡片状态与动作。 */
|
|
179
187
|
export declare class AiRouterCardController {
|
|
180
|
-
private readonly
|
|
188
|
+
private readonly form;
|
|
181
189
|
private readonly storeCredential;
|
|
182
190
|
private readonly store;
|
|
183
|
-
constructor(
|
|
191
|
+
constructor(form: ConfigForm<AiRouterCardSettings>, storeCredential: CredentialStorePort);
|
|
184
192
|
inject(): AiRouterCardFace;
|
|
185
193
|
private submitLogin;
|
|
186
194
|
/** 切换 Key:先落 keyId 配置,再触发一次刷新(Host 以新 keyId 重拉额度与模型)。 */
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { DEFAULT_FEATURE_ICON, type FeatureRegistry, type MenuFeature } from './feature-registry.ts';
|
|
2
|
+
export { DEFAULT_FEATURE_ICON };
|
|
3
|
+
import { type MenuPreferences } from './menu-preferences.ts';
|
|
2
4
|
/** 中央区 takeover 的活动页标记(纯 CSS 隐藏原生内容的开关)。 */
|
|
3
5
|
export declare const ACTIVE_PAGE_ATTRIBUTE = "data-hiwork-active-page";
|
|
4
6
|
/**
|
|
@@ -8,16 +10,59 @@ export declare const ACTIVE_PAGE_ATTRIBUTE = "data-hiwork-active-page";
|
|
|
8
10
|
* 即完全恢复,不会留下任何内联 `display:none`。隐藏原生按钮同理走属性规则。
|
|
9
11
|
*/
|
|
10
12
|
export declare const featureCenterCss: string;
|
|
13
|
+
/**
|
|
14
|
+
* 原生中央面板(DSH layout 的 main 面板)控制器。
|
|
15
|
+
*
|
|
16
|
+
* 由 `index.ts` 用 `ctx.layout`(selectPanel / panelInfo)与 `ctx.slots`(main 槽的
|
|
17
|
+
* 注册探测)实现;feature-center 只依赖这个窄面,便于测试桩与优雅降级。缺省
|
|
18
|
+
* (未接线)时官方入口完全不收编:不隐藏原生行、不渲染代理项,行为与旧版一致。
|
|
19
|
+
*/
|
|
20
|
+
export interface NativePanelControl {
|
|
21
|
+
/** 官方「插件」面板当前是否已注册(main 槽存在 key `plugins`)。 */
|
|
22
|
+
pluginsAvailable(): boolean;
|
|
23
|
+
/** 当前是否有任何原生面板处于激活(`activePanelId` 非 null)。 */
|
|
24
|
+
anyActive(): boolean;
|
|
25
|
+
/** 当前激活的是不是「插件」面板。 */
|
|
26
|
+
pluginsActive(): boolean;
|
|
27
|
+
/** 打开官方「插件」面板。 */
|
|
28
|
+
openPlugins(): void;
|
|
29
|
+
/** 回到会话(`activePanelId` → null)。 */
|
|
30
|
+
returnToConversation(): void;
|
|
31
|
+
/** 面板注册与激活状态的变化源(main 槽 + panelInfo 合并订阅)。 */
|
|
32
|
+
subscribe(listener: () => void): () => void;
|
|
33
|
+
}
|
|
11
34
|
/** 中央区适配器配置。 */
|
|
12
35
|
export interface FeatureCenterOptions {
|
|
13
36
|
/** hiwork-core 的扩展注册表。 */
|
|
14
37
|
readonly registry: FeatureRegistry;
|
|
15
38
|
/** 宿主会话导航(`ctx.sessions.open`)。 */
|
|
16
39
|
readonly openNativeSession: (sessionId: string) => void;
|
|
40
|
+
/**
|
|
41
|
+
* 追加的菜单条目(不进注册表、无中央页渲染的代理项)。
|
|
42
|
+
*
|
|
43
|
+
* 当前只有官方「插件」面板的代理项({@link nativePluginsMenuItem} 的返回值,可用性
|
|
44
|
+
* 由调用方判断);它以 `MenuFeature` 形态原样参与偏好分组/排序/三态,点击由菜单
|
|
45
|
+
* 适配器按 id 路由到 `nativePanel.openPlugins()`。每次重建菜单时现取。
|
|
46
|
+
*/
|
|
47
|
+
readonly additionalFeatures?: () => readonly MenuFeature[];
|
|
48
|
+
/** 原生中央面板控制器;缺省不收编官方入口。 */
|
|
49
|
+
readonly nativePanel?: NativePanelControl;
|
|
50
|
+
/**
|
|
51
|
+
* 侧栏菜单偏好的当前快照(`hiwork-menu` settings 命名空间,见 `menu-preferences.ts`)。
|
|
52
|
+
*
|
|
53
|
+
* 每次重建菜单时现取(像 `registry.list()` 一样),不缓存:偏好写盘 → 订阅通知 → 这里读到的
|
|
54
|
+
* 就是新值。缺省(未接线 / 老调用方)等价于「用户从未自定义」,菜单按默认分组渲染。
|
|
55
|
+
*/
|
|
56
|
+
readonly preferences?: () => MenuPreferences;
|
|
17
57
|
/** 「新会话」菜单文案;缺省「新会话」。 */
|
|
18
58
|
readonly newSessionLabel?: () => string;
|
|
19
59
|
/** 语言变化源(`ctx.locale.subscribe`);变化时重建菜单文案。 */
|
|
20
60
|
readonly subscribeLocale?: (listener: () => void) => () => void;
|
|
61
|
+
/**
|
|
62
|
+
* 偏好变化源(`hiwork-menu` scope 的 subscribe)。变化时重建菜单:
|
|
63
|
+
* 设置面板里改一项,侧栏立刻跟着变,不需要重启。
|
|
64
|
+
*/
|
|
65
|
+
readonly subscribePreferences?: (listener: () => void) => () => void;
|
|
21
66
|
/** 未就绪时的重试间隔(毫秒),缺省 200。 */
|
|
22
67
|
readonly retryIntervalMs?: number;
|
|
23
68
|
/** 未就绪时的重试总预算(毫秒),缺省 10_000;预算耗尽后只靠 observer 自愈。 */
|
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
import type { ReactNode } from 'react';
|
|
15
15
|
/** 缺省排序权重:未声明 `order` 的 feature 排在声明了正权重之后、按 id 稳定。 */
|
|
16
16
|
export declare const DEFAULT_FEATURE_ORDER = 1000;
|
|
17
|
+
/**
|
|
18
|
+
* 扩展没有提供 `icon` 时的兜底图标:官方 `ic_ds_plugin_pinwheel_outline_16`(官方在用的
|
|
19
|
+
* 扩展/插件 glyph),保证折叠态既有可辨识图形,又和侧栏原生图标同一套语言。
|
|
20
|
+
* 侧栏菜单与设置页「侧栏菜单」分区共用这一份;官方「插件」代理项({@link nativePluginsMenuItem})
|
|
21
|
+
* 也用它——那本来就是同一个 glyph。
|
|
22
|
+
*/
|
|
23
|
+
export declare const DEFAULT_FEATURE_ICON = "<svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M7.84457 5.06199C11.6605 4.93876 14.7962 6.14848 14.8484 7.76397C14.8875 8.97461 13.1838 10.0696 10.7215 10.5942\" stroke=\"currentColor\" stroke-width=\"1.2\"></path><path d=\"M5.12742 8.07731C5.00419 4.26138 6.21391 1.12568 7.8294 1.07351C9.04004 1.03441 10.135 2.73808 10.6596 5.20037\" stroke=\"currentColor\" stroke-width=\"1.2\"></path><path d=\"M8.02457 10.6802C4.20865 10.8034 1.07294 9.5937 1.02077 7.97821C0.981678 6.76758 2.68535 5.67262 5.14763 5.14798\" stroke=\"currentColor\" stroke-width=\"1.2\"></path><path d=\"M10.7476 7.89535C10.8708 11.7113 9.66109 14.847 8.0456 14.8991C6.83496 14.9382 5.74 13.2346 5.21536 10.7723\" stroke=\"currentColor\" stroke-width=\"1.2\"></path></svg>";
|
|
17
24
|
/**
|
|
18
25
|
* 业务插件渲染中央页时拿到的上下文。
|
|
19
26
|
*
|
|
@@ -26,9 +33,15 @@ export interface FeatureRenderContext {
|
|
|
26
33
|
/** 回到原生「新会话」首页;调用前会先退出 takeover。 */
|
|
27
34
|
readonly openNativeHome: () => void;
|
|
28
35
|
}
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
36
|
+
/**
|
|
37
|
+
* 菜单条目的结构子集:分组与排序的纯函数(`menu-preferences.ts`)只认这几样。
|
|
38
|
+
*
|
|
39
|
+
* {@link HiWorkFeature} 是它的超集(多一个 `render`);官方「插件」面板的代理项
|
|
40
|
+
* ({@link nativePluginsMenuItem})不是注册表成员、没有中央页渲染,以本形态流入
|
|
41
|
+
* 同一套分组与偏好算法。
|
|
42
|
+
*/
|
|
43
|
+
export interface MenuFeature {
|
|
44
|
+
/** 稳定 id,同时作为菜单项标识。 */
|
|
32
45
|
readonly id: string;
|
|
33
46
|
/** 菜单文案;函数形式以便语言切换后重新求值。 */
|
|
34
47
|
readonly label: () => string;
|
|
@@ -36,9 +49,29 @@ export interface HiWorkFeature {
|
|
|
36
49
|
readonly icon?: string;
|
|
37
50
|
/** 菜单排序权重,越小越靠前;缺省 {@link DEFAULT_FEATURE_ORDER}。 */
|
|
38
51
|
readonly order?: number;
|
|
52
|
+
}
|
|
53
|
+
/** 一个可注册的 HiWork 扩展入口。 */
|
|
54
|
+
export interface HiWorkFeature extends MenuFeature {
|
|
39
55
|
/** 渲染中央页内容;抛错由中央区适配器捕获并恢复原生会话区。 */
|
|
40
56
|
readonly render: (context: FeatureRenderContext) => ReactNode;
|
|
41
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* 官方「插件」管理页(DSH `dsh-client-ui-plugin-manager`)在 HiWork 菜单里的代理项。
|
|
60
|
+
*
|
|
61
|
+
* 它**不是注册表成员**:官方页面由 DSH layout 的 main 面板机制承载(面板 id
|
|
62
|
+
* `plugins`,见 `dsh-client-ui-plugin-manager` 的 `slots.inject("main")` 注册),
|
|
63
|
+
* 点击走 `layout.selectPanel('plugins')` 而不是本插件的中央区 takeover。把它做成
|
|
64
|
+
* {@link MenuFeature} 形态是为了让它**原样参与**菜单偏好的分组/排序/三态——用户
|
|
65
|
+
* 可以像拖动其它入口一样拖动它、把它收进「更多」或隐藏。
|
|
66
|
+
*
|
|
67
|
+
* 默认 order 70:排在六个业务 feature(10–60)之后。文案与「更多」同款硬编码中文
|
|
68
|
+
* (hiwork-core 未注册 locale 命名空间)。
|
|
69
|
+
*/
|
|
70
|
+
export declare const NATIVE_PLUGINS_MENU_ID = "__native_plugins__";
|
|
71
|
+
export declare const NATIVE_PLUGINS_MENU_LABEL = "\u63D2\u4EF6";
|
|
72
|
+
export declare const NATIVE_PLUGINS_MENU_ORDER = 70;
|
|
73
|
+
/** 官方「插件」面板的菜单代理项(每次调用返回新对象;可用性由调用方判断后再拼入列表)。 */
|
|
74
|
+
export declare function nativePluginsMenuItem(): MenuFeature;
|
|
42
75
|
/** 扩展注册表。 */
|
|
43
76
|
export interface FeatureRegistry {
|
|
44
77
|
/** 注册一个 feature;返回幂等 disposer。id 为空或重复时抛错。 */
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话内「生成图片」工具调用的**专属卡片**(`tool.call.toolview`,按线上工具名 keyed)。
|
|
3
|
+
*
|
|
4
|
+
* 为什么必须有它:生图是分钟级的动作,走 shell 的通用工具行时用户只看到「它在调一个
|
|
5
|
+
* 工具」,看不到提示词、看不到进度、更看不到图。卡片把一次 `image_generate` 变成可读
|
|
6
|
+
* 凭据:图片(走同源文件路由 `<base>/api/file?path=…`)、提示词(可复制)、尺寸/耗时、
|
|
7
|
+
* 落盘路径(`~` 缩写),失败时给**逐字**来自 spec §6.6 的中文原因。
|
|
8
|
+
*
|
|
9
|
+
* 三条实测得来的形状约束:
|
|
10
|
+
* 1. **keyed 槽位按线上工具名分派**,未认领的键落回通用工具行——所以为本工具注册是纯
|
|
11
|
+
* 增量行为。键拼错**不报错**,只是卡片永不渲染(knowledge 已踩过),因此工具名是
|
|
12
|
+
* 本文件的导出常量,注册段与测试都从它取;
|
|
13
|
+
* 2. `block` 有三个阶段(`preparing` / `start` / `result`,见 ui-tool 的
|
|
14
|
+
* `ToolCallPhaseProps`):preparing 连参数都还没有,start 有 `argsRaw`(原始 JSON
|
|
15
|
+
* 字符串),result 才有 `content`。所以「提示词」在 start 阶段就要能显示,卡片不能
|
|
16
|
+
* 等到结果回来才画;
|
|
17
|
+
* 3. 结果里**结构化数据**与给模型的文本在同一个 `content` 数组里(Host 侧 `output.render`
|
|
18
|
+
* 的第一条是给人/模型看的文本、第二条是与 `GenerateOutcome` 同形的 JSON,照
|
|
19
|
+
* knowledge 的既有形状)。解析器按「找到第一个带 ok 字段的 JSON 对象」认领,另外对
|
|
20
|
+
* 「只有文本、里面带 `<path>…</path>`」的老实现在渲染层兜底——**绝不**因为拿不到
|
|
21
|
+
* 结构化结果就把图藏起来。
|
|
22
|
+
*
|
|
23
|
+
* client bundle 纯度:本文件不 import 任何 `@deepseek-ai/*` 运行时值,也不 import Host
|
|
24
|
+
* 代码(块/结果的形状按结构重声明)。
|
|
25
|
+
*/
|
|
26
|
+
import { type ReactElement } from 'react';
|
|
27
|
+
/**
|
|
28
|
+
* 认领的线上工具名。**必须**与 Host `src/tool-names.ts` 的 `IMAGE_TOOL_NAMES` 逐字一致
|
|
29
|
+
* (拼错不报错,只是卡片永不渲染;由测试锁定这个字面量)。
|
|
30
|
+
*/
|
|
31
|
+
export declare const IMAGE_GENERATE_TOOL_NAME = "image_generate";
|
|
32
|
+
/** 认领的 keyed 槽位名(ui-tool 声明的「按线上工具名分派」的那个座位)。 */
|
|
33
|
+
export declare const TOOL_VIEW_SLOT = "tool.call.toolview";
|
|
34
|
+
/** 卡片标题(hiwork-core 未注册 locale 命名空间,文案硬编码中文)。 */
|
|
35
|
+
export declare const IMAGE_CARD_TITLE = "\u751F\u6210\u56FE\u7247";
|
|
36
|
+
/** 拿不到任何错误码时的兜底文案。 */
|
|
37
|
+
export declare const IMAGE_CARD_GENERIC_ERROR = "\u56FE\u7247\u751F\u6210\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
38
|
+
/**
|
|
39
|
+
* 网关/主机错误码 → 卡片文案(**逐字**来自 spec §6.6 的表,不要改字)。
|
|
40
|
+
*
|
|
41
|
+
* `IMAGE_BUDGET_EXCEEDED` 一行覆盖两种成因是有意的:网关的 402 由四个计费哨兵共用
|
|
42
|
+
* (未分配预算 / 额度不足 / 模型价格缺失 / 模型已下架),后两条不是员工的额度问题,
|
|
43
|
+
* 但它们与额度问题回同一个 402 与同一种响应体。桌面端**不做**中文文案分流(那会把
|
|
44
|
+
* 中文变成机器契约),所以文案对两种成因都成立——员工无论哪种都得到正确的指路。
|
|
45
|
+
*/
|
|
46
|
+
export declare const IMAGE_ERROR_TEXT: Readonly<Record<string, string>>;
|
|
47
|
+
/** 可以原样再试一次的错误码(其余要么找管理员、要么重新登录)。 */
|
|
48
|
+
export declare const IMAGE_RETRYABLE_CODES: readonly string[];
|
|
49
|
+
/**
|
|
50
|
+
* 除了错误文案,还要提示「重新登录」的码(spec §6.6 那条括号注;措辞用母表的原话)。
|
|
51
|
+
* 单独的 hint 而不是另写一句文案:`SESSION_EXPIRED` 在表里与 `IMAGE_UPSTREAM_ERROR`
|
|
52
|
+
* **共用同一行文案**,自创一句就是改冻结文案。
|
|
53
|
+
*/
|
|
54
|
+
export declare const IMAGE_RELOGIN_CODES: readonly string[];
|
|
55
|
+
/** 重新登录的提示(与「可以再试一次」同位置展示)。 */
|
|
56
|
+
export declare const IMAGE_RELOGIN_HINT = "\u8BF7\u91CD\u65B0\u767B\u5F55";
|
|
57
|
+
/** 该错误码是否要额外提示重新登录。 */
|
|
58
|
+
export declare function needsRelogin(code: string): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* 错误码 → 可展示文案。
|
|
61
|
+
* @param code - 网关/Host 的错误码。
|
|
62
|
+
* @param message - Host 给的 message(表里没有的码才用;空则兜底文案)。
|
|
63
|
+
* @returns 逐字文案。
|
|
64
|
+
*/
|
|
65
|
+
export declare function imageErrorText(code: string, message?: string): string;
|
|
66
|
+
/** 该错误码是否「可以再试一次」。 */
|
|
67
|
+
export declare function isRetryableImageError(code: string): boolean;
|
|
68
|
+
/** 结果内容块(只建模本卡片用得到的两个字段)。 */
|
|
69
|
+
export interface ImageContentBlockLike {
|
|
70
|
+
readonly type?: unknown;
|
|
71
|
+
readonly text?: unknown;
|
|
72
|
+
}
|
|
73
|
+
/** `tool.call.toolview` 的 `block`(preparing / start / result 三阶段)。 */
|
|
74
|
+
export interface ImageToolBlockLike {
|
|
75
|
+
/** settled 时是 `'tool-result'`(`ToolResultNode`)。 */
|
|
76
|
+
readonly kind?: unknown;
|
|
77
|
+
/** 新宿主显式给出的阶段(`'preparing' | 'start' | 'result'`)。 */
|
|
78
|
+
readonly phase?: unknown;
|
|
79
|
+
readonly callId?: unknown;
|
|
80
|
+
/** start 阶段就是它(原始 JSON 字符串)。 */
|
|
81
|
+
readonly argsRaw?: unknown;
|
|
82
|
+
/** settled 时从 `call` 回填参数(窗口截断时 `call` 为 null)。 */
|
|
83
|
+
readonly call?: {
|
|
84
|
+
readonly name?: unknown;
|
|
85
|
+
readonly argsRaw?: unknown;
|
|
86
|
+
} | null;
|
|
87
|
+
readonly content?: readonly ImageContentBlockLike[];
|
|
88
|
+
readonly isError?: unknown;
|
|
89
|
+
readonly error?: {
|
|
90
|
+
readonly name?: unknown;
|
|
91
|
+
readonly code?: unknown;
|
|
92
|
+
} | null;
|
|
93
|
+
}
|
|
94
|
+
/** 卡片收到的属性:owner 面(block / toolName / cwd / home / openFile / inspect)。 */
|
|
95
|
+
export interface ImageToolCardProps {
|
|
96
|
+
readonly block?: ImageToolBlockLike | undefined;
|
|
97
|
+
readonly toolName?: string | undefined;
|
|
98
|
+
/** 会话工作区根(相对路径摘要用,本卡片只画绝对路径,保留字段以免形状漂移)。 */
|
|
99
|
+
readonly cwd?: string | undefined;
|
|
100
|
+
/** 宿主账号 home;POSIX home 下的绝对路径显示为 `~/…`。 */
|
|
101
|
+
readonly home?: string | undefined;
|
|
102
|
+
/** 打开一个文件(宿主提供;缺失时不渲染「打开」按钮)。 */
|
|
103
|
+
readonly openFile?: ((path: string, options?: unknown) => void) | undefined;
|
|
104
|
+
/** 在轨迹视图里查看这次调用(可选能力;缺失时不渲染)。 */
|
|
105
|
+
readonly inspect?: (() => void) | undefined;
|
|
106
|
+
}
|
|
107
|
+
/** 卡片模型的单张图。 */
|
|
108
|
+
export interface ImageCardFile {
|
|
109
|
+
/** 绝对路径(只画绝对路径;相对路径/远端 URL 不画)。 */
|
|
110
|
+
readonly path: string;
|
|
111
|
+
/** 同源文件路由地址。 */
|
|
112
|
+
readonly url: string;
|
|
113
|
+
/** 图片替代文本(**必须非空**:空 alt 对读屏用户等于没有图)。 */
|
|
114
|
+
readonly alt: string;
|
|
115
|
+
readonly byteSize: number | undefined;
|
|
116
|
+
readonly mimeType: string;
|
|
117
|
+
readonly revisedPrompt: string | undefined;
|
|
118
|
+
}
|
|
119
|
+
/** 卡片模型(纯函数产物:渲染只读它,解析逻辑可单测)。 */
|
|
120
|
+
export interface ImageToolCardModel {
|
|
121
|
+
readonly state: 'running' | 'ok' | 'error' | 'stopped';
|
|
122
|
+
readonly prompt: string;
|
|
123
|
+
readonly size: string;
|
|
124
|
+
readonly model: string;
|
|
125
|
+
readonly generationMs: number | undefined;
|
|
126
|
+
readonly files: readonly ImageCardFile[];
|
|
127
|
+
/** 失败文案(逐字,见 {@link IMAGE_ERROR_TEXT});无失败为 null。 */
|
|
128
|
+
readonly error: string | null;
|
|
129
|
+
readonly errorCode: string;
|
|
130
|
+
readonly retryable: boolean;
|
|
131
|
+
}
|
|
132
|
+
/** 解析原始 JSON 参数串;非法/缺省一律给空对象(running 阶段没有参数是正常态)。 */
|
|
133
|
+
export declare function parseImageArgs(argsRaw: unknown): Record<string, unknown>;
|
|
134
|
+
/**
|
|
135
|
+
* 从结果的 `content` 里认领结构化结果(与 `GenerateOutcome` 同形):取第一个
|
|
136
|
+
* 「带布尔 `ok`」的 JSON 对象。逐块尝试是有意的——Host 侧第一条块是给模型的文本,
|
|
137
|
+
* 结构化那份在后面;顺序变化不影响认领。
|
|
138
|
+
*/
|
|
139
|
+
export declare function parseImageOutcome(content: readonly ImageContentBlockLike[] | undefined): Record<string, unknown> | null;
|
|
140
|
+
/**
|
|
141
|
+
* 从结果文本里取 `<path>…</path>`(Host 给模型的那段文本用的就是这个标记)。
|
|
142
|
+
* 只在**没有**结构化结果时兜底:图已经生成,绝不能因为拿不到结构化结果就藏起来。
|
|
143
|
+
*/
|
|
144
|
+
export declare function parseImagePathsFromText(content: readonly ImageContentBlockLike[] | undefined): string[];
|
|
145
|
+
/** POSIX 绝对路径(`/a/b`)或带盘符的 Windows 绝对路径(`C:\a\b` / `C:/a/b`)。 */
|
|
146
|
+
export declare function isAbsoluteImagePath(path: string): boolean;
|
|
147
|
+
/**
|
|
148
|
+
* 同源文件路由地址:**逐字**复刻平台 `fileMediaUrl(base, path)` 的文法
|
|
149
|
+
* (`<base>/api/file?path=<encodeURIComponent(绝对路径)>`)。
|
|
150
|
+
*
|
|
151
|
+
* 为什么自己拼而不 import 官方工具:client bundle 对 `@deepseek-ai/*` 只能 type-only
|
|
152
|
+
* import(打包期擦除,运行时模块表不认),capability 插件已有同款先例(office 的
|
|
153
|
+
* `resource-address.ts` 按官方文法复刻解析)。编码是**必须**的:路径里有空格/中文/`#`
|
|
154
|
+
* 时不编码会让路由拿到截断的路径(图 404,且报错发生在宿主侧、查不到原因)。
|
|
155
|
+
* @param path - 绝对路径。
|
|
156
|
+
* @param baseURI - 文档基准地址(`document.baseURI`)。
|
|
157
|
+
* @returns 可直接放进 `<img src>` 的地址;路径不是绝对路径时返回空串(调用方不画)。
|
|
158
|
+
*/
|
|
159
|
+
export declare function imageFileUrl(path: string, baseURI: string): string;
|
|
160
|
+
/**
|
|
161
|
+
* 把绝对路径里的 home 前缀缩写成 `~`(会话卡片与画廊页共用)。
|
|
162
|
+
* @param path - 绝对路径。
|
|
163
|
+
* @param home - 宿主账号 home;缺省/空则原样返回。
|
|
164
|
+
* @returns 缩写后的展示路径。
|
|
165
|
+
*/
|
|
166
|
+
export declare function abbreviateHome(path: string, home?: string): string;
|
|
167
|
+
/** 耗时展示:秒级保留一位小数(生图是分钟级的动作,毫秒数字没有意义)。 */
|
|
168
|
+
export declare function formatGenerationMs(ms: number | undefined): string;
|
|
169
|
+
/** 体积展示(1 位小数;生图常态 1–3MB)。 */
|
|
170
|
+
export declare function formatByteSize(bytes: number | undefined): string;
|
|
171
|
+
/**
|
|
172
|
+
* 一次 `image_generate` 调用 → 卡片模型。
|
|
173
|
+
* @param block - keyed 槽位的块(三阶段之一)。
|
|
174
|
+
* @param baseURI - 文档基准地址(构造文件路由用)。
|
|
175
|
+
* @returns 卡片模型。
|
|
176
|
+
*/
|
|
177
|
+
export declare function toImageCardModel(block: ImageToolBlockLike | undefined, baseURI: string): ImageToolCardModel;
|
|
178
|
+
/** 复制结果文案(idle 同时是按钮的默认提示)。 */
|
|
179
|
+
export declare const COPY_HINTS: {
|
|
180
|
+
readonly idle: "";
|
|
181
|
+
readonly copied: "已复制";
|
|
182
|
+
readonly failed: "复制失败";
|
|
183
|
+
};
|
|
184
|
+
/** 复制提示词到剪贴板;不可用或写入被拒一律 false。 */
|
|
185
|
+
export declare function copyPromptText(text: string): Promise<boolean>;
|
|
186
|
+
/**
|
|
187
|
+
* 一次 `image_generate` 调用的卡片。
|
|
188
|
+
* @param props - keyed 槽位的 owner 面(block / toolName / home / openFile / inspect)。
|
|
189
|
+
* @returns 卡片元素。
|
|
190
|
+
*/
|
|
191
|
+
export declare function ImageToolCard(props: ImageToolCardProps): ReactElement;
|
|
192
|
+
/**
|
|
193
|
+
* keyed 座位的窄端口。
|
|
194
|
+
*
|
|
195
|
+
* 为什么不用真实类型:`tool.call.toolview` 由 **ui-tool**(`@deepseek-ai/dsh-client-ui-tool`)
|
|
196
|
+
* 声明,而 core 的开发依赖里没有这个包(它只是运行时模块表里的一项);照 knowledge 的
|
|
197
|
+
* 做法用结构契约(`ctx.slots.inject(name, cb)` + `register({name,key}, Component)`)把
|
|
198
|
+
* 两个回调串起来,既拿到编译期检查,又不新增依赖。注册选项里**不带** `locale`:
|
|
199
|
+
* core 未注册 locale 命名空间,文案硬编码中文(与设置页分区同一口径)。
|
|
200
|
+
*/
|
|
201
|
+
export interface ToolViewSlotsPort {
|
|
202
|
+
inject(key: string, callback: () => (() => void) | void): unknown;
|
|
203
|
+
register(options: {
|
|
204
|
+
name: string;
|
|
205
|
+
key: string;
|
|
206
|
+
}, component: (props: ImageToolCardProps) => ReactElement): () => void;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* 注册会话内的生图卡片(按线上工具名 keyed 的座位,session 作用域由槽位声明决定)。
|
|
210
|
+
*
|
|
211
|
+
* 注册是**纯增量**的:未认领的键落回 shell 的通用工具行,所以这里只认领
|
|
212
|
+
* `image_generate` 一个键。宿主环境没有 slots 服务时静默跳过(卡片不出现,但绝不让
|
|
213
|
+
* 插件装配失败)。
|
|
214
|
+
* @param ctx - 任意带 `slots` 的上下文(`apply` 传真实 ctx;测试传假桩)。
|
|
215
|
+
*/
|
|
216
|
+
export declare function installImageToolCard(ctx: {
|
|
217
|
+
readonly slots?: unknown;
|
|
218
|
+
}): void;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** 注入样式的固定 id(同一页面只保留一份;与 feature-center 同一套防重手法)。 */
|
|
2
|
+
export declare const IMAGES_STYLE_ID = "hiwork-core-images-styles";
|
|
3
|
+
/** 归属标记里的 css 身份(官方预设的 `包名/文件名.css` 形态)。 */
|
|
4
|
+
export declare const IMAGES_STYLE_TAG_ID = "hiwork-core/images.css";
|
|
5
|
+
/**
|
|
6
|
+
* 确保生图能力的两份样式已注入。幂等:同一文档只注入一次。
|
|
7
|
+
* @param doc - 目标文档(测试可注入假 document)。
|
|
8
|
+
*/
|
|
9
|
+
export declare function ensureImagesStyle(doc: Document): void;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 「我的图片」画廊页的客户端取数层:经 `/hiwork-image` loopback RPC 读**既有**的网关
|
|
3
|
+
* 员工自助端点(`/api/me/images`、`/api/me/images/:id/file`),做信封拆解与形状归一。
|
|
4
|
+
*
|
|
5
|
+
* 铁律(与 `usage-data.ts` 同一套):
|
|
6
|
+
* - 客户端**不持有**会话 cookie,也**不直接 fetch 网关**——一切经 `connection.rpc`;
|
|
7
|
+
* - **不缓存图片字节**:`fetch` 每次现取、用完即弃(不落 DSH 附件存储、不进会话日志);
|
|
8
|
+
* `save` 只在用户显式点击时写盘(写盘在 Host 半边);
|
|
9
|
+
* - 失败绝**不**降级成空数据:错误带稳定 code,视图把 message 直接显示在行内。
|
|
10
|
+
*
|
|
11
|
+
* client bundle 纯度:本文件不 import 任何 `@deepseek-ai/*` 运行时值,也不 import
|
|
12
|
+
* Host 代码(`ImageRow` 等按结构重声明,字段名与网关 JSON / Host 契约一致)。
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* RPC 频道;必须与 Host `src/image-rpc.ts` 的频道常量一致(冻结契约 `/hiwork-image`)。
|
|
16
|
+
* 由本文件与 `tests/image-gallery.spec.tsx` 的字面量断言锁定。
|
|
17
|
+
*/
|
|
18
|
+
export declare const IMAGE_CHANNEL = "/hiwork-image";
|
|
19
|
+
/**
|
|
20
|
+
* 端点白名单(**冻结契约**,与 Host 的 `IMAGE_RPC_ENDPOINTS` 逐字同序)。
|
|
21
|
+
*
|
|
22
|
+
* `status` / `generate` 供会话内工具链路(那个链路不经过本文件),画廊页只用后三个:
|
|
23
|
+
* `list` / `fetch` / `save`。这里整表列出是为了让「一个能力一个频道、端点表只有一处」
|
|
24
|
+
* 这条约定在客户端也看得见。
|
|
25
|
+
*/
|
|
26
|
+
export declare const IMAGE_RPC_ENDPOINTS: readonly ["status", "generate", "list", "fetch", "save"];
|
|
27
|
+
/** 端点名。 */
|
|
28
|
+
export type ImageEndpoint = (typeof IMAGE_RPC_ENDPOINTS)[number];
|
|
29
|
+
/** 画廊页默认分页(spec §6.5:默认 20)。 */
|
|
30
|
+
export declare const IMAGE_LIST_PAGE_SIZE = 20;
|
|
31
|
+
/** 分页上限(与 Host 侧一致;再大也只是白拉流量)。 */
|
|
32
|
+
export declare const IMAGE_LIST_MAX_PAGE_SIZE = 100;
|
|
33
|
+
/** 拿不到可展示原因时的兜底文案。 */
|
|
34
|
+
export declare const IMAGE_DATA_ERROR_TEXT = "\u56FE\u7247\u8BB0\u5F55\u6682\u65F6\u65E0\u6CD5\u83B7\u53D6\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5";
|
|
35
|
+
/**
|
|
36
|
+
* 一条历史记录(**生产形状**:网关 `/api/me/images` 的 item 在补了 `size` 投影之后
|
|
37
|
+
* 的字段集;`tests/image-gallery.spec.tsx` 的夹具必须按这个形状构造)。
|
|
38
|
+
*/
|
|
39
|
+
export interface ImageRow {
|
|
40
|
+
readonly id: number;
|
|
41
|
+
readonly prompt: string;
|
|
42
|
+
readonly revisedPrompt?: string | undefined;
|
|
43
|
+
readonly model: string;
|
|
44
|
+
readonly size: string;
|
|
45
|
+
readonly format: string;
|
|
46
|
+
readonly mimeType: string;
|
|
47
|
+
readonly byteSize: number;
|
|
48
|
+
readonly generationMs: number;
|
|
49
|
+
/** RFC3339(网关 `created_at`)。 */
|
|
50
|
+
readonly createdAt: string;
|
|
51
|
+
}
|
|
52
|
+
/** `list` 端点出参。 */
|
|
53
|
+
export interface ImageListPage {
|
|
54
|
+
readonly items: readonly ImageRow[];
|
|
55
|
+
readonly total: number;
|
|
56
|
+
readonly page: number;
|
|
57
|
+
readonly pageSize: number;
|
|
58
|
+
}
|
|
59
|
+
/** `fetch` 端点出参(图片本体,base64 无 data URL 前缀)。 */
|
|
60
|
+
export interface ImageBytes {
|
|
61
|
+
readonly dataBase64: string;
|
|
62
|
+
readonly mimeType: string;
|
|
63
|
+
readonly byteSize: number;
|
|
64
|
+
}
|
|
65
|
+
/** 宿主 loopback RPC 调用面(结构兼容 `ctx.connection.rpc`)。 */
|
|
66
|
+
export interface ImageRpcPort {
|
|
67
|
+
call(channel: string, endpoint: string, payload: unknown, signal?: AbortSignal): Promise<unknown>;
|
|
68
|
+
}
|
|
69
|
+
/** 画廊页消费的最小取数面(便于测试注入假实现)。 */
|
|
70
|
+
export interface ImageDataSource {
|
|
71
|
+
list(page: number, pageSize?: number): Promise<ImageListPage>;
|
|
72
|
+
fetch(id: number): Promise<ImageBytes>;
|
|
73
|
+
save(id: number, workspaceId?: string): Promise<{
|
|
74
|
+
path: string;
|
|
75
|
+
}>;
|
|
76
|
+
}
|
|
77
|
+
/** 带稳定 code 的取数错误(message 已由 Host 消毒,可直接展示)。 */
|
|
78
|
+
export declare class ImageRpcError extends Error {
|
|
79
|
+
readonly code: string;
|
|
80
|
+
constructor(code: string, message: string);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 解开 DSH Connection 的结果信封(与 `usage-data.ts` 的 `unwrapUsageResult` 同语义)。
|
|
84
|
+
* 宿主返回 `{ok:true,value}` / `{ok:false,error:{code,message}}`;形状不合法时抛通用失败,
|
|
85
|
+
* 绝不把未知结构当数据渲染。
|
|
86
|
+
*/
|
|
87
|
+
export declare function unwrapImageResult<T>(value: unknown): T;
|
|
88
|
+
/** 任意异常 → 可展示的 `{code, message}`(传输层异常不暴露原始 message)。 */
|
|
89
|
+
export declare function imageErrorOf(error: unknown): {
|
|
90
|
+
code: string;
|
|
91
|
+
message: string;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* 归一 `list` 的返回:字段逐个显式取(**不**把未知结构当数据)。
|
|
95
|
+
* `content` 的形状由网关决定,这里只保证「视图拿到的一定是生产形状」。
|
|
96
|
+
*/
|
|
97
|
+
export declare function normalizeImageListPage(value: unknown, fallbackPage: number, fallbackPageSize: number): ImageListPage;
|
|
98
|
+
/** 归一 `fetch` 的返回:base64 为空视为失败(空图不是可展示的结果)。 */
|
|
99
|
+
export declare function normalizeImageBytes(value: unknown): ImageBytes;
|
|
100
|
+
/**
|
|
101
|
+
* `/hiwork-image` 的三个只读/落盘端点(画廊页用)。**无缓存**:每次调用现取。
|
|
102
|
+
*/
|
|
103
|
+
export declare class ImageGallerySource implements ImageDataSource {
|
|
104
|
+
private readonly rpc;
|
|
105
|
+
constructor(rpc: ImageRpcPort);
|
|
106
|
+
/** 分页拉取历史记录(`page` 从 1 开始;`pageSize` 上限 100)。 */
|
|
107
|
+
list(page?: number, pageSize?: number): Promise<ImageListPage>;
|
|
108
|
+
/** 取一张图的本体(**点哪张拉哪张**;超 20MiB 由 Host 回 `IMAGE_TOO_LARGE`)。 */
|
|
109
|
+
fetch(id: number): Promise<ImageBytes>;
|
|
110
|
+
/** 把一张图存到工作区(缺省落默认工作区,由 Host 决定)。 */
|
|
111
|
+
save(id: number, workspaceId?: string): Promise<{
|
|
112
|
+
path: string;
|
|
113
|
+
}>;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* 图片本体 → data URL(预览用)。**不写盘、不指向本地文件**:字节是本次 `fetch` 的结果,
|
|
117
|
+
* `<img src>` 里只能是它自己。
|
|
118
|
+
* @param bytes - `fetch` 的返回。
|
|
119
|
+
* @returns `data:<mime>;base64,<本体>`。
|
|
120
|
+
*/
|
|
121
|
+
export declare function imageDataUrl(bytes: ImageBytes): string;
|
|
122
|
+
/** 体积展示(1 位小数);缺失/非法返回 `—` 之外的空串由调用方决定。 */
|
|
123
|
+
export declare function formatImageBytes(bytes: number | undefined): string;
|
|
124
|
+
/** 列表行的提示词摘要(超长截断;空提示词给「(无提示词)」)。 */
|
|
125
|
+
export declare function imagePromptSummary(prompt: string, limit?: number): string;
|
|
126
|
+
/** 记录时间:非法时间串返回空串(视图渲染 `—`)。 */
|
|
127
|
+
export declare function imageTimeLabel(createdAt: string): string;
|