@h-ai/ui 0.1.0-alpha.12 → 0.1.0-alpha.15
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 +20 -4
- package/dist/components/compounds/BottomNav.svelte +21 -1
- package/dist/components/compounds/BottomNav.svelte.d.ts +5 -0
- package/dist/components/primitives/Switch.svelte +5 -0
- package/dist/components/scenes/ai/AiDocumentDownloadMenu.svelte +1 -0
- package/dist/components/scenes/ai/AiDocumentEditor.svelte +1210 -145
- package/dist/components/scenes/ai/MarkdownRenderer.svelte +0 -4
- package/dist/components/scenes/ai/document-types.d.ts +28 -2
- package/dist/components/scenes/ai/editor-markdown-extensions.js +19 -4
- package/dist/components/scenes/crud/CrudFilterBar.svelte +1 -1
- package/dist/components/scenes/crud/CrudPage.svelte +27 -22
- package/dist/components/scenes/crud/CrudPage.svelte.d.ts +22 -15
- package/dist/components/scenes/crud/index.d.ts +2 -0
- package/dist/components/scenes/crud/index.js +1 -0
- package/dist/components/scenes/crud/nav-adapter.d.ts +36 -0
- package/dist/components/scenes/crud/nav-adapter.js +31 -0
- package/dist/components/scenes/crypto/EncryptedInput.svelte +2 -4
- package/dist/components/scenes/crypto/EncryptedInput.svelte.d.ts +1 -1
- package/dist/components/scenes/crypto/HashDisplay.svelte +1 -2
- package/dist/components/scenes/crypto/SignatureDisplay.svelte +1 -3
- package/dist/components/scenes/iam/UserProfile.svelte +7 -1
- package/dist/components/scenes/types.d.ts +3 -3
- package/dist/messages/en-US.json +5 -0
- package/dist/messages/zh-CN.json +5 -0
- package/dist/styles/mobile.css +28 -0
- package/package.json +3 -3
|
@@ -33,9 +33,6 @@
|
|
|
33
33
|
parseMarkdown(content, { enableHighlight, showCopyButton, breaks }),
|
|
34
34
|
)
|
|
35
35
|
|
|
36
|
-
/** 容器引用 */
|
|
37
|
-
let containerEl: HTMLDivElement | undefined = $state()
|
|
38
|
-
|
|
39
36
|
/**
|
|
40
37
|
* 事件代理:处理代码块复制按钮点击
|
|
41
38
|
*/
|
|
@@ -66,7 +63,6 @@
|
|
|
66
63
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
67
64
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
68
65
|
<div
|
|
69
|
-
bind:this={containerEl}
|
|
70
66
|
class={cn('hai-markdown', className)}
|
|
71
67
|
onclick={handleClick}
|
|
72
68
|
>
|
|
@@ -65,7 +65,7 @@ export type MarkdownBlockFormatKind = 'heading' | 'bullet';
|
|
|
65
65
|
/**
|
|
66
66
|
* Rich block-style actions used by the current editor toolbar.
|
|
67
67
|
*/
|
|
68
|
-
export type MarkdownBlockStyleKind = 'paragraph' | 'heading1' | 'heading2' | 'heading3' | 'heading4';
|
|
68
|
+
export type MarkdownBlockStyleKind = 'paragraph' | 'heading1' | 'heading2' | 'heading3' | 'heading4' | 'orderedList' | 'bulletList';
|
|
69
69
|
/**
|
|
70
70
|
* Inline format actions for the editor toolbar.
|
|
71
71
|
* `highlight` and `link` stay in the public union so older integrations keep compiling.
|
|
@@ -75,6 +75,14 @@ export type MarkdownInlineFormatKind = 'bold' | 'italic' | 'strike' | 'underline
|
|
|
75
75
|
* Paragraph alignment actions for the editor toolbar.
|
|
76
76
|
*/
|
|
77
77
|
export type MarkdownTextAlignKind = 'left' | 'center' | 'right' | 'justify';
|
|
78
|
+
export interface MarkdownSelectionSnapshot {
|
|
79
|
+
/** Selection start offset within the plain-text document. */
|
|
80
|
+
start: number;
|
|
81
|
+
/** Selection end offset within the plain-text document. */
|
|
82
|
+
end: number;
|
|
83
|
+
/** Selected plain text, used as a fallback when structure changes. */
|
|
84
|
+
text: string;
|
|
85
|
+
}
|
|
78
86
|
/**
|
|
79
87
|
* 颜色面板一次只会修改前景色或背景色中的一个维度。
|
|
80
88
|
*/
|
|
@@ -83,6 +91,18 @@ export interface MarkdownColorFormatRequest {
|
|
|
83
91
|
target: 'text' | 'background';
|
|
84
92
|
/** 颜色值为空时表示恢复默认。 */
|
|
85
93
|
value: string | null;
|
|
94
|
+
/** 当前工具条记住的选中文本,供外层在 DOM 重建后兜底恢复定位。 */
|
|
95
|
+
selectedText?: string;
|
|
96
|
+
/** 当前工具条记住的精确选区偏移,优先用于重复文本场景的恢复。 */
|
|
97
|
+
selectionSnapshot?: MarkdownSelectionSnapshot;
|
|
98
|
+
}
|
|
99
|
+
export interface MarkdownLinkFormatRequest {
|
|
100
|
+
/** 为空时表示移除当前链接。 */
|
|
101
|
+
href: string | null;
|
|
102
|
+
/** 当前工具条记住的精确选区偏移。 */
|
|
103
|
+
selectionSnapshot?: MarkdownSelectionSnapshot;
|
|
104
|
+
/** 当前工具条记住的选中文本,供宿主做文本兜底。 */
|
|
105
|
+
selectedText?: string;
|
|
86
106
|
}
|
|
87
107
|
export interface MarkdownToolbarDownloadAction {
|
|
88
108
|
/** Action id used to identify the download target. */
|
|
@@ -124,6 +144,8 @@ export interface AiDocumentEditorProps {
|
|
|
124
144
|
breaks?: boolean;
|
|
125
145
|
/** Whether to show the left outline panel. */
|
|
126
146
|
showOutline?: boolean;
|
|
147
|
+
/** Whether to show numbering prefixes in outline items. */
|
|
148
|
+
showOutlineNumbering?: boolean;
|
|
127
149
|
/** Whether to show the top toolbar. */
|
|
128
150
|
showToolbar?: boolean;
|
|
129
151
|
/** Initial outline collapsed state. */
|
|
@@ -191,7 +213,11 @@ export interface AiDocumentEditorProps {
|
|
|
191
213
|
/** Paragraph alignment handler from the selection toolbar. */
|
|
192
214
|
onapplyalignment?: (kind: MarkdownTextAlignKind) => void;
|
|
193
215
|
/** Link add / edit / remove handler from the selection toolbar. */
|
|
194
|
-
onapplylink?:
|
|
216
|
+
onapplylink?: BivariantCallback<[
|
|
217
|
+
href: string | null,
|
|
218
|
+
selectionSnapshot?: MarkdownSelectionSnapshot,
|
|
219
|
+
selectedText?: string
|
|
220
|
+
], void>;
|
|
195
221
|
/** Text or background color handler from the selection toolbar. */
|
|
196
222
|
onapplycolor?: (request: MarkdownColorFormatRequest) => void;
|
|
197
223
|
/** Selection copy handler for custom behavior. */
|
|
@@ -7,7 +7,8 @@ const HEX_COLOR_REGEX = /^#(?:[0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
|
7
7
|
*/
|
|
8
8
|
const ALIGN_VALUE_REGEX = /^(?:left|center|right|justify)$/i;
|
|
9
9
|
const HAI_ALIGN_BLOCK_REGEX = /^<hai-align\s+value="(left|center|right|justify)">\n?([\s\S]+?)\n?<\/hai-align>\n{0,2}/i;
|
|
10
|
-
const HAI_SPAN_INLINE_REGEX = /^<hai-span(
|
|
10
|
+
const HAI_SPAN_INLINE_REGEX = /^<hai-span\b([^>]*)>([\s\S]+?)<\/hai-span>/i;
|
|
11
|
+
const HAI_SPAN_ATTR_REGEX = /\b(color|bg)="([^"]+)"/gi;
|
|
11
12
|
const HAI_UNDERLINE_REGEX = /^\+\+((?:\\.|[\s\S])+?)\+\+/;
|
|
12
13
|
const HAI_HIGHLIGHT_REGEX = /^==((?:\\.|[\s\S])+?)==/;
|
|
13
14
|
/**
|
|
@@ -55,6 +56,21 @@ function sanitizeAlign(value) {
|
|
|
55
56
|
? normalized
|
|
56
57
|
: undefined;
|
|
57
58
|
}
|
|
59
|
+
function parseHaiSpanAttributes(rawAttributes) {
|
|
60
|
+
let color;
|
|
61
|
+
let background;
|
|
62
|
+
for (const match of rawAttributes.matchAll(HAI_SPAN_ATTR_REGEX)) {
|
|
63
|
+
const [, attrName, attrValue] = match;
|
|
64
|
+
if (attrName === 'color') {
|
|
65
|
+
color = sanitizeColor(attrValue) ?? color;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (attrName === 'bg') {
|
|
69
|
+
background = sanitizeColor(attrValue) ?? background;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { color, background };
|
|
73
|
+
}
|
|
58
74
|
/**
|
|
59
75
|
* ChatWorkspace 编辑器会把“非标准 Markdown”能力写成自定义标签,
|
|
60
76
|
* 这里把它们注册为 marked 扩展,保证普通 Markdown 和编辑器增强能力共享同一条渲染链路。
|
|
@@ -101,14 +117,13 @@ export function createEditorMarkdownExtensions() {
|
|
|
101
117
|
if (!match) {
|
|
102
118
|
return undefined;
|
|
103
119
|
}
|
|
104
|
-
const color =
|
|
105
|
-
const background = sanitizeColor(match[2]);
|
|
120
|
+
const { color, background } = parseHaiSpanAttributes(match[1] ?? '');
|
|
106
121
|
return {
|
|
107
122
|
type: 'haiSpan',
|
|
108
123
|
raw: match[0],
|
|
109
124
|
color,
|
|
110
125
|
background,
|
|
111
|
-
tokens: this.lexer.inlineTokens(match[
|
|
126
|
+
tokens: this.lexer.inlineTokens(match[2]),
|
|
112
127
|
};
|
|
113
128
|
},
|
|
114
129
|
renderer(token) {
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
<Select
|
|
80
80
|
class='min-w-32'
|
|
81
81
|
value={String(filterValues[field.id] ?? '')}
|
|
82
|
-
onchange={
|
|
82
|
+
onchange={value => handleFilterChange(field.id, value)}
|
|
83
83
|
>
|
|
84
84
|
<option value="">{uiM('crud_filter_all')}</option>
|
|
85
85
|
{#each opts as opt}
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
<script lang='ts'>
|
|
14
14
|
import type { Snippet } from 'svelte'
|
|
15
15
|
import type { Size } from '../../../types.js'
|
|
16
|
-
import {
|
|
17
|
-
import { page } from '$app/state'
|
|
16
|
+
import type { NavAdapter } from './nav-adapter.js'
|
|
18
17
|
import { uiM } from '../../../messages.js'
|
|
19
18
|
import Card from '../../compounds/Card.svelte'
|
|
20
19
|
import DataTable from '../../compounds/DataTable.svelte'
|
|
@@ -26,6 +25,7 @@
|
|
|
26
25
|
import CrudDetailDrawer from './CrudDetailDrawer.svelte'
|
|
27
26
|
import CrudEditDrawer from './CrudEditDrawer.svelte'
|
|
28
27
|
import CrudFilterBar from './CrudFilterBar.svelte'
|
|
28
|
+
import { createBrowserNavAdapter } from './nav-adapter.js'
|
|
29
29
|
|
|
30
30
|
// ─── 类型定义 ───
|
|
31
31
|
|
|
@@ -49,7 +49,15 @@
|
|
|
49
49
|
order?: number
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
// 使用 method shorthand 语法(双向协变)以兼容来自 @h-ai/kit 的 CrudOperations 类型
|
|
53
|
+
// 详见:https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html#strict-function-types
|
|
54
|
+
type CrudApi = {
|
|
55
|
+
list(params: Record<string, unknown>): Promise<{ items: Record<string, unknown>[], total: number, page: number, pageSize: number }>
|
|
56
|
+
get?(id: string): Promise<Record<string, unknown> | null>
|
|
57
|
+
create?(data: Record<string, unknown>): Promise<Record<string, unknown>>
|
|
58
|
+
update?(id: string, data: Record<string, unknown>): Promise<Record<string, unknown>>
|
|
59
|
+
remove?(id: string): Promise<void>
|
|
60
|
+
}
|
|
53
61
|
|
|
54
62
|
type CrudDef = {
|
|
55
63
|
resource: {
|
|
@@ -60,13 +68,7 @@
|
|
|
60
68
|
defaultPageSize?: number
|
|
61
69
|
searchable?: boolean
|
|
62
70
|
searchPlaceholder?: string | (() => string)
|
|
63
|
-
api:
|
|
64
|
-
list: AnyApiFunc
|
|
65
|
-
get?: AnyApiFunc
|
|
66
|
-
create?: AnyApiFunc
|
|
67
|
-
update?: AnyApiFunc
|
|
68
|
-
remove?: AnyApiFunc
|
|
69
|
-
}
|
|
71
|
+
api: CrudApi
|
|
70
72
|
}
|
|
71
73
|
getListFields: () => FieldDef[]
|
|
72
74
|
getFilterFields: () => FieldDef[]
|
|
@@ -75,13 +77,7 @@
|
|
|
75
77
|
getDetailFields: () => FieldDef[]
|
|
76
78
|
toTableColumns: () => Array<{ key: string, label: string, width?: string, align?: string, render?: (item: Record<string, unknown>) => string }>
|
|
77
79
|
getDefaultValues: () => Record<string, unknown>
|
|
78
|
-
api:
|
|
79
|
-
list: AnyApiFunc
|
|
80
|
-
get?: AnyApiFunc
|
|
81
|
-
create?: AnyApiFunc
|
|
82
|
-
update?: AnyApiFunc
|
|
83
|
-
remove?: AnyApiFunc
|
|
84
|
-
}
|
|
80
|
+
api: CrudApi
|
|
85
81
|
}
|
|
86
82
|
|
|
87
83
|
// ─── Props ───
|
|
@@ -101,6 +97,7 @@
|
|
|
101
97
|
onafterdelete,
|
|
102
98
|
onerror,
|
|
103
99
|
basePath = '',
|
|
100
|
+
nav = createBrowserNavAdapter(),
|
|
104
101
|
class: className = '',
|
|
105
102
|
}: {
|
|
106
103
|
crud: CrudDef
|
|
@@ -117,6 +114,13 @@
|
|
|
117
114
|
onafterdelete?: (item: Record<string, unknown>) => void
|
|
118
115
|
onerror?: (error: string) => void
|
|
119
116
|
basePath?: string
|
|
117
|
+
/**
|
|
118
|
+
* 路由适配器:用于读取当前 pathname、同步过滤参数到 URL、列表刷新。
|
|
119
|
+
* 默认使用 `createBrowserNavAdapter()`(基于 `globalThis.location`)。
|
|
120
|
+
* SvelteKit 应用应传入 `createSvelteKitNavAdapter()`(来自 `@h-ai/kit/client`),
|
|
121
|
+
* 以获得客户端跳转 + invalidateAll 体验。
|
|
122
|
+
*/
|
|
123
|
+
nav?: NavAdapter
|
|
120
124
|
class?: string
|
|
121
125
|
} = $props()
|
|
122
126
|
|
|
@@ -144,7 +148,7 @@
|
|
|
144
148
|
let filterValues = $state<Record<string, unknown>>({})
|
|
145
149
|
|
|
146
150
|
// 基础路径
|
|
147
|
-
const currentBasePath = $derived(basePath ||
|
|
151
|
+
const currentBasePath = $derived(basePath || nav.pathname)
|
|
148
152
|
|
|
149
153
|
// ─── 工具函数 ───
|
|
150
154
|
|
|
@@ -194,7 +198,8 @@
|
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
const qs = params.toString()
|
|
197
|
-
|
|
201
|
+
const url = `${currentBasePath}${qs ? `?${qs}` : ''}`
|
|
202
|
+
void Promise.resolve(nav.navigate(url)).then(() => nav.refresh?.())
|
|
198
203
|
}
|
|
199
204
|
|
|
200
205
|
function handleSearch(search: string) {
|
|
@@ -258,7 +263,7 @@
|
|
|
258
263
|
const submitted = (result ?? {}) as Record<string, unknown>
|
|
259
264
|
closeDrawer()
|
|
260
265
|
onaftersubmit?.(submitted, 'create')
|
|
261
|
-
await
|
|
266
|
+
await nav.refresh?.()
|
|
262
267
|
}
|
|
263
268
|
else if (drawerMode === 'edit' && crud.api.update && selectedItem) {
|
|
264
269
|
const id = String(selectedItem[keyField])
|
|
@@ -266,7 +271,7 @@
|
|
|
266
271
|
const submitted = (result ?? {}) as Record<string, unknown>
|
|
267
272
|
closeDrawer()
|
|
268
273
|
onaftersubmit?.(submitted, 'edit')
|
|
269
|
-
await
|
|
274
|
+
await nav.refresh?.()
|
|
270
275
|
}
|
|
271
276
|
}
|
|
272
277
|
catch (e) {
|
|
@@ -311,7 +316,7 @@
|
|
|
311
316
|
closeDrawer()
|
|
312
317
|
}
|
|
313
318
|
onafterdelete?.(deleted)
|
|
314
|
-
await
|
|
319
|
+
await nav.refresh?.()
|
|
315
320
|
}
|
|
316
321
|
catch (e) {
|
|
317
322
|
const msg = e instanceof Error ? e.message : uiM('crud_delete_failed')
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Snippet } from 'svelte';
|
|
2
2
|
import type { Size } from '../../../types.js';
|
|
3
|
+
import type { NavAdapter } from './nav-adapter.js';
|
|
3
4
|
type FieldDef = {
|
|
4
5
|
id: string;
|
|
5
6
|
label: string | (() => string);
|
|
@@ -31,7 +32,18 @@ type FieldDef = {
|
|
|
31
32
|
defaultValue?: unknown;
|
|
32
33
|
order?: number;
|
|
33
34
|
};
|
|
34
|
-
type
|
|
35
|
+
type CrudApi = {
|
|
36
|
+
list(params: Record<string, unknown>): Promise<{
|
|
37
|
+
items: Record<string, unknown>[];
|
|
38
|
+
total: number;
|
|
39
|
+
page: number;
|
|
40
|
+
pageSize: number;
|
|
41
|
+
}>;
|
|
42
|
+
get?(id: string): Promise<Record<string, unknown> | null>;
|
|
43
|
+
create?(data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
44
|
+
update?(id: string, data: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
45
|
+
remove?(id: string): Promise<void>;
|
|
46
|
+
};
|
|
35
47
|
type CrudDef = {
|
|
36
48
|
resource: {
|
|
37
49
|
name: string;
|
|
@@ -41,13 +53,7 @@ type CrudDef = {
|
|
|
41
53
|
defaultPageSize?: number;
|
|
42
54
|
searchable?: boolean;
|
|
43
55
|
searchPlaceholder?: string | (() => string);
|
|
44
|
-
api:
|
|
45
|
-
list: AnyApiFunc;
|
|
46
|
-
get?: AnyApiFunc;
|
|
47
|
-
create?: AnyApiFunc;
|
|
48
|
-
update?: AnyApiFunc;
|
|
49
|
-
remove?: AnyApiFunc;
|
|
50
|
-
};
|
|
56
|
+
api: CrudApi;
|
|
51
57
|
};
|
|
52
58
|
getListFields: () => FieldDef[];
|
|
53
59
|
getFilterFields: () => FieldDef[];
|
|
@@ -62,13 +68,7 @@ type CrudDef = {
|
|
|
62
68
|
render?: (item: Record<string, unknown>) => string;
|
|
63
69
|
}>;
|
|
64
70
|
getDefaultValues: () => Record<string, unknown>;
|
|
65
|
-
api:
|
|
66
|
-
list: AnyApiFunc;
|
|
67
|
-
get?: AnyApiFunc;
|
|
68
|
-
create?: AnyApiFunc;
|
|
69
|
-
update?: AnyApiFunc;
|
|
70
|
-
remove?: AnyApiFunc;
|
|
71
|
-
};
|
|
71
|
+
api: CrudApi;
|
|
72
72
|
};
|
|
73
73
|
type $$ComponentProps = {
|
|
74
74
|
crud: CrudDef;
|
|
@@ -95,6 +95,13 @@ type $$ComponentProps = {
|
|
|
95
95
|
onafterdelete?: (item: Record<string, unknown>) => void;
|
|
96
96
|
onerror?: (error: string) => void;
|
|
97
97
|
basePath?: string;
|
|
98
|
+
/**
|
|
99
|
+
* 路由适配器:用于读取当前 pathname、同步过滤参数到 URL、列表刷新。
|
|
100
|
+
* 默认使用 `createBrowserNavAdapter()`(基于 `globalThis.location`)。
|
|
101
|
+
* SvelteKit 应用应传入 `createSvelteKitNavAdapter()`(来自 `@h-ai/kit/client`),
|
|
102
|
+
* 以获得客户端跳转 + invalidateAll 体验。
|
|
103
|
+
*/
|
|
104
|
+
nav?: NavAdapter;
|
|
98
105
|
class?: string;
|
|
99
106
|
};
|
|
100
107
|
/**
|
|
@@ -10,3 +10,5 @@ export { default as CrudDetailDrawer } from './CrudDetailDrawer.svelte';
|
|
|
10
10
|
export { default as CrudEditDrawer } from './CrudEditDrawer.svelte';
|
|
11
11
|
export { default as CrudFilterBar } from './CrudFilterBar.svelte';
|
|
12
12
|
export { default as CrudPage } from './CrudPage.svelte';
|
|
13
|
+
export type { NavAdapter } from './nav-adapter.js';
|
|
14
|
+
export { createBrowserNavAdapter } from './nav-adapter.js';
|
|
@@ -9,3 +9,4 @@ export { default as CrudDetailDrawer } from './CrudDetailDrawer.svelte';
|
|
|
9
9
|
export { default as CrudEditDrawer } from './CrudEditDrawer.svelte';
|
|
10
10
|
export { default as CrudFilterBar } from './CrudFilterBar.svelte';
|
|
11
11
|
export { default as CrudPage } from './CrudPage.svelte';
|
|
12
|
+
export { createBrowserNavAdapter } from './nav-adapter.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @h-ai/ui — CRUD 导航适配器
|
|
3
|
+
*
|
|
4
|
+
* `CrudPage` 通过 `nav` prop 接入宿主路由系统,避免对任何具体框架(SvelteKit、
|
|
5
|
+
* 自实现 hash router 等)形成强依赖。
|
|
6
|
+
*
|
|
7
|
+
* - SvelteKit 应用:传入 `createSvelteKitNavAdapter()`(从 `@h-ai/kit/client` 导入)。
|
|
8
|
+
* - 纯浏览器 / Tauri / Capacitor:传入自定义实现或使用默认 `createBrowserNavAdapter()`。
|
|
9
|
+
*
|
|
10
|
+
* @module nav-adapter
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* 路由适配器接口。`CrudPage` 通过此接口完成:
|
|
14
|
+
* - 读取当前 pathname(作为列表 URL 的 basePath 兜底)
|
|
15
|
+
* - 调用 `navigate(url)` 同步搜索 / 过滤 / 分页参数到地址栏
|
|
16
|
+
* - 调用 `refresh()` 在写操作后重新拉取列表数据
|
|
17
|
+
*/
|
|
18
|
+
export interface NavAdapter {
|
|
19
|
+
/** 当前页面路径(不含 query / hash),用于推导 `basePath`。 */
|
|
20
|
+
readonly pathname: string;
|
|
21
|
+
/** 跳转到指定 URL(可带 query),通常用于更新 URL 上的搜索/分页参数。 */
|
|
22
|
+
readonly navigate: (url: string) => void | Promise<void>;
|
|
23
|
+
/** 写操作完成后触发列表刷新;未提供时由调用方自行通过 `onaftersubmit` / `onafterdelete` 处理。 */
|
|
24
|
+
readonly refresh?: () => void | Promise<void>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 创建基于浏览器 `globalThis.location` 的默认导航适配器。
|
|
28
|
+
*
|
|
29
|
+
* 适用场景:纯 SPA(无 SvelteKit)、Tauri、Capacitor 等可以直接读写 `location` 的环境。
|
|
30
|
+
*
|
|
31
|
+
* - `pathname` 读 `globalThis.location?.pathname`,SSR 下安全降级为 `''`。
|
|
32
|
+
* - `navigate(url)` 调 `globalThis.location.assign(url)`;如需客户端路由(hash router 等),
|
|
33
|
+
* 宿主应自行实现一个 `NavAdapter` 传入。
|
|
34
|
+
* - 不提供 `refresh`:浏览器默认导航本身会触发页面加载,调用方需在导航前/后重新拉取数据。
|
|
35
|
+
*/
|
|
36
|
+
export declare function createBrowserNavAdapter(): NavAdapter;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @h-ai/ui — CRUD 导航适配器
|
|
3
|
+
*
|
|
4
|
+
* `CrudPage` 通过 `nav` prop 接入宿主路由系统,避免对任何具体框架(SvelteKit、
|
|
5
|
+
* 自实现 hash router 等)形成强依赖。
|
|
6
|
+
*
|
|
7
|
+
* - SvelteKit 应用:传入 `createSvelteKitNavAdapter()`(从 `@h-ai/kit/client` 导入)。
|
|
8
|
+
* - 纯浏览器 / Tauri / Capacitor:传入自定义实现或使用默认 `createBrowserNavAdapter()`。
|
|
9
|
+
*
|
|
10
|
+
* @module nav-adapter
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* 创建基于浏览器 `globalThis.location` 的默认导航适配器。
|
|
14
|
+
*
|
|
15
|
+
* 适用场景:纯 SPA(无 SvelteKit)、Tauri、Capacitor 等可以直接读写 `location` 的环境。
|
|
16
|
+
*
|
|
17
|
+
* - `pathname` 读 `globalThis.location?.pathname`,SSR 下安全降级为 `''`。
|
|
18
|
+
* - `navigate(url)` 调 `globalThis.location.assign(url)`;如需客户端路由(hash router 等),
|
|
19
|
+
* 宿主应自行实现一个 `NavAdapter` 传入。
|
|
20
|
+
* - 不提供 `refresh`:浏览器默认导航本身会触发页面加载,调用方需在导航前/后重新拉取数据。
|
|
21
|
+
*/
|
|
22
|
+
export function createBrowserNavAdapter() {
|
|
23
|
+
return {
|
|
24
|
+
get pathname() {
|
|
25
|
+
return globalThis.location?.pathname ?? '';
|
|
26
|
+
},
|
|
27
|
+
navigate(url) {
|
|
28
|
+
globalThis.location?.assign(url);
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -11,9 +11,7 @@
|
|
|
11
11
|
<script lang='ts'>
|
|
12
12
|
import type { Size } from '../../../types.js'
|
|
13
13
|
import { uiM } from '../../../messages.js'
|
|
14
|
-
import BareButton from '../../primitives/
|
|
15
|
-
import IconButton from '../../primitives/IconButton.svelte'
|
|
16
|
-
import Input from '../../primitives/Input.svelte'
|
|
14
|
+
import { BareButton, IconButton, Input } from '../../primitives/index.js'
|
|
17
15
|
|
|
18
16
|
interface Props {
|
|
19
17
|
/** 原始值 */
|
|
@@ -29,7 +27,7 @@
|
|
|
29
27
|
/** 是否显示加密结果 */
|
|
30
28
|
showEncrypted?: boolean
|
|
31
29
|
/** 加密算法 */
|
|
32
|
-
algorithm?:
|
|
30
|
+
algorithm?: 'SM2' | 'SM4'
|
|
33
31
|
/** 自定义类名 */
|
|
34
32
|
class?: string
|
|
35
33
|
/** 加密函数 */
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
import type { HashDisplayProps } from '../types.js'
|
|
13
13
|
import { uiM } from '../../../messages.js'
|
|
14
14
|
import { cn } from '../../../utils.js'
|
|
15
|
-
import Badge from '../../primitives/
|
|
16
|
-
import IconButton from '../../primitives/IconButton.svelte'
|
|
15
|
+
import { Badge, IconButton } from '../../primitives/index.js'
|
|
17
16
|
|
|
18
17
|
const {
|
|
19
18
|
value = '',
|
|
@@ -12,9 +12,7 @@
|
|
|
12
12
|
import type { SignatureDisplayProps } from '../types.js'
|
|
13
13
|
import { uiM } from '../../../messages.js'
|
|
14
14
|
import { cn } from '../../../utils.js'
|
|
15
|
-
import Badge from '../../primitives/
|
|
16
|
-
|
|
17
|
-
import IconButton from '../../primitives/IconButton.svelte'
|
|
15
|
+
import { Badge, IconButton } from '../../primitives/index.js'
|
|
18
16
|
|
|
19
17
|
const {
|
|
20
18
|
signature = '',
|
|
@@ -144,6 +144,7 @@
|
|
|
144
144
|
const file = e.currentTarget.files?.[0]
|
|
145
145
|
if (file) {
|
|
146
146
|
onavatarchange?.(file)
|
|
147
|
+
e.currentTarget.value = ''
|
|
147
148
|
}
|
|
148
149
|
}
|
|
149
150
|
</script>
|
|
@@ -170,9 +171,14 @@
|
|
|
170
171
|
type='file'
|
|
171
172
|
class='file-input file-input-sm w-full max-w-xs'
|
|
172
173
|
accept='image/*'
|
|
174
|
+
disabled={loading}
|
|
173
175
|
onchange={handleAvatarChange}
|
|
174
176
|
/>
|
|
175
|
-
|
|
177
|
+
{#if errors.avatar}
|
|
178
|
+
<p class='text-xs text-error mt-1' role='alert'>{errors.avatar}</p>
|
|
179
|
+
{:else}
|
|
180
|
+
<p class='text-xs text-base-content/60 mt-1'>{uiM('user_profile_avatar_hint')}</p>
|
|
181
|
+
{/if}
|
|
176
182
|
</div>
|
|
177
183
|
{/if}
|
|
178
184
|
</div>
|
|
@@ -499,7 +499,7 @@ export interface EncryptedInputProps {
|
|
|
499
499
|
/** 是否显示加密结果 */
|
|
500
500
|
showEncrypted?: boolean;
|
|
501
501
|
/** 加密算法 */
|
|
502
|
-
algorithm?: 'SM2' | 'SM4'
|
|
502
|
+
algorithm?: 'SM2' | 'SM4';
|
|
503
503
|
/** 加密函数 */
|
|
504
504
|
onencrypt?: (value: string) => Promise<string>;
|
|
505
505
|
/** 自定义类名 */
|
|
@@ -520,7 +520,7 @@ export interface HashDisplayProps {
|
|
|
520
520
|
/** 哈希值 */
|
|
521
521
|
value?: string;
|
|
522
522
|
/** 算法 */
|
|
523
|
-
algorithm?: 'SM3'
|
|
523
|
+
algorithm?: 'SM3';
|
|
524
524
|
/** 标签 */
|
|
525
525
|
label?: string;
|
|
526
526
|
/** 是否可复制 */
|
|
@@ -560,7 +560,7 @@ export interface SignatureDisplayProps {
|
|
|
560
560
|
/** 公钥 */
|
|
561
561
|
publicKey?: string;
|
|
562
562
|
/** 算法 */
|
|
563
|
-
algorithm?: 'SM2'
|
|
563
|
+
algorithm?: 'SM2';
|
|
564
564
|
/** 验证状态 */
|
|
565
565
|
verified?: boolean;
|
|
566
566
|
/** 是否显示公钥 */
|
package/dist/messages/en-US.json
CHANGED
|
@@ -210,6 +210,8 @@
|
|
|
210
210
|
"markdown_format_heading_3": "Heading 3",
|
|
211
211
|
"markdown_format_heading_4": "Heading 4",
|
|
212
212
|
"markdown_format_bullet": "Bullets",
|
|
213
|
+
"markdown_format_ordered_list": "Ordered list",
|
|
214
|
+
"markdown_format_bullet_list": "Bullet list",
|
|
213
215
|
"markdown_format_bold": "Bold",
|
|
214
216
|
"markdown_format_strike": "Strike",
|
|
215
217
|
"markdown_format_italic": "Italic",
|
|
@@ -224,6 +226,7 @@
|
|
|
224
226
|
"markdown_align_justify": "Justify",
|
|
225
227
|
"markdown_text_color": "Text color",
|
|
226
228
|
"markdown_background_color": "Background color",
|
|
229
|
+
"markdown_background_none": "No background",
|
|
227
230
|
"markdown_reset_default": "Reset default",
|
|
228
231
|
"markdown_color_dialog_label": "Text and background color",
|
|
229
232
|
"markdown_link_dialog_label": "Edit link",
|
|
@@ -231,6 +234,8 @@
|
|
|
231
234
|
"markdown_link_placeholder": "Paste or type a link",
|
|
232
235
|
"markdown_link_apply": "Apply link",
|
|
233
236
|
"markdown_link_remove": "Remove link",
|
|
237
|
+
"markdown_link_open": "Open link",
|
|
238
|
+
"markdown_link_edit": "Edit link",
|
|
234
239
|
"markdown_copy_selection": "Copy",
|
|
235
240
|
"markdown_annotation": "Annotate",
|
|
236
241
|
"markdown_run_preview": "Run Preview",
|
package/dist/messages/zh-CN.json
CHANGED
|
@@ -210,6 +210,8 @@
|
|
|
210
210
|
"markdown_format_heading_3": "三级标题",
|
|
211
211
|
"markdown_format_heading_4": "四级标题",
|
|
212
212
|
"markdown_format_bullet": "列表",
|
|
213
|
+
"markdown_format_ordered_list": "有序列表",
|
|
214
|
+
"markdown_format_bullet_list": "无序列表",
|
|
213
215
|
"markdown_format_bold": "加粗",
|
|
214
216
|
"markdown_format_strike": "删除线",
|
|
215
217
|
"markdown_format_italic": "斜体",
|
|
@@ -224,6 +226,7 @@
|
|
|
224
226
|
"markdown_align_justify": "两端对齐",
|
|
225
227
|
"markdown_text_color": "字体颜色",
|
|
226
228
|
"markdown_background_color": "背景颜色",
|
|
229
|
+
"markdown_background_none": "无背景",
|
|
227
230
|
"markdown_reset_default": "恢复默认",
|
|
228
231
|
"markdown_color_dialog_label": "文本和背景颜色",
|
|
229
232
|
"markdown_link_dialog_label": "编辑链接",
|
|
@@ -231,6 +234,8 @@
|
|
|
231
234
|
"markdown_link_placeholder": "输入或粘贴链接地址",
|
|
232
235
|
"markdown_link_apply": "应用链接",
|
|
233
236
|
"markdown_link_remove": "移除链接",
|
|
237
|
+
"markdown_link_open": "打开链接",
|
|
238
|
+
"markdown_link_edit": "修改链接",
|
|
234
239
|
"markdown_copy_selection": "复制",
|
|
235
240
|
"markdown_annotation": "注释",
|
|
236
241
|
"markdown_run_preview": "运行预览",
|
package/dist/styles/mobile.css
CHANGED
|
@@ -103,3 +103,31 @@
|
|
|
103
103
|
overflow-y: auto;
|
|
104
104
|
-webkit-overflow-scrolling: touch;
|
|
105
105
|
}
|
|
106
|
+
|
|
107
|
+
/* ─── 主流移动端 App 壳布局 ─── */
|
|
108
|
+
.hai-mobile-viewport {
|
|
109
|
+
min-height: 100dvh;
|
|
110
|
+
background:
|
|
111
|
+
radial-gradient(circle at top left, color-mix(in srgb, hsl(var(--p)) 12%, transparent), transparent 32rem),
|
|
112
|
+
hsl(var(--b2));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.hai-mobile-shell {
|
|
116
|
+
width: 100%;
|
|
117
|
+
min-height: 100dvh;
|
|
118
|
+
margin-inline: auto;
|
|
119
|
+
display: flex;
|
|
120
|
+
flex-direction: column;
|
|
121
|
+
background: hsl(var(--b1));
|
|
122
|
+
box-shadow:
|
|
123
|
+
0 0 0 1px color-mix(in srgb, currentColor 6%, transparent),
|
|
124
|
+
0 24px 72px rgb(15 23 42 / 10%);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.hai-mobile-main {
|
|
128
|
+
flex: 1 1 auto;
|
|
129
|
+
min-height: 0;
|
|
130
|
+
overflow-y: auto;
|
|
131
|
+
overscroll-behavior: contain;
|
|
132
|
+
-webkit-overflow-scrolling: touch;
|
|
133
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@h-ai/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.15",
|
|
5
5
|
"description": "Hai Framework UI component library for Svelte 5 Runes.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"svelte": "./dist/index.js",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
|
-
"svelte": "^5.
|
|
36
|
+
"svelte": "^5.55.5"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@internationalized/date": "^3.8.0",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"marked": "^18.0.0",
|
|
45
45
|
"shiki": "^4.0.2",
|
|
46
46
|
"tailwind-merge": "^3.5.0",
|
|
47
|
-
"@h-ai/core": "0.1.0-alpha.
|
|
47
|
+
"@h-ai/core": "0.1.0-alpha.15"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@sveltejs/package": "^2.5.7",
|