@loom-framework/core 0.1.0-alpha.157 → 0.1.0-alpha.159

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 (125) hide show
  1. package/builtin-skills/app-skill/SKILL.md +1 -0
  2. package/builtin-skills/app-skill/references/auth.md +12 -0
  3. package/builtin-skills/app-skill/references/models.md +12 -0
  4. package/builtin-skills/app-skill/references/process-builder.md +1 -1
  5. package/builtin-skills/app-skill/references/process.md +6 -6
  6. package/dist/backend/ai/session-manager.d.ts +5 -2
  7. package/dist/backend/ai/session-manager.d.ts.map +1 -1
  8. package/dist/backend/ai/session-manager.js +8 -3
  9. package/dist/backend/ai/session-manager.js.map +1 -1
  10. package/dist/backend/auth/rbac.d.ts.map +1 -1
  11. package/dist/backend/auth/rbac.js +1 -0
  12. package/dist/backend/auth/rbac.js.map +1 -1
  13. package/dist/backend/auth/token-store.d.ts +2 -1
  14. package/dist/backend/auth/token-store.d.ts.map +1 -1
  15. package/dist/backend/auth/token-store.js +2 -2
  16. package/dist/backend/auth/token-store.js.map +1 -1
  17. package/dist/backend/auth/user-store.d.ts +2 -1
  18. package/dist/backend/auth/user-store.d.ts.map +1 -1
  19. package/dist/backend/auth/user-store.js +2 -2
  20. package/dist/backend/auth/user-store.js.map +1 -1
  21. package/dist/backend/index.d.ts +5 -0
  22. package/dist/backend/index.d.ts.map +1 -1
  23. package/dist/backend/index.js +81 -50
  24. package/dist/backend/index.js.map +1 -1
  25. package/dist/backend/loom-paths.d.ts +28 -0
  26. package/dist/backend/loom-paths.d.ts.map +1 -0
  27. package/dist/backend/loom-paths.js +45 -0
  28. package/dist/backend/loom-paths.js.map +1 -0
  29. package/dist/backend/notifications/notification-storage.d.ts +4 -2
  30. package/dist/backend/notifications/notification-storage.d.ts.map +1 -1
  31. package/dist/backend/notifications/notification-storage.js +7 -5
  32. package/dist/backend/notifications/notification-storage.js.map +1 -1
  33. package/dist/backend/observe/logger.d.ts +4 -2
  34. package/dist/backend/observe/logger.d.ts.map +1 -1
  35. package/dist/backend/observe/logger.js +12 -7
  36. package/dist/backend/observe/logger.js.map +1 -1
  37. package/dist/backend/process/engine.d.ts +5 -2
  38. package/dist/backend/process/engine.d.ts.map +1 -1
  39. package/dist/backend/process/engine.js +9 -7
  40. package/dist/backend/process/engine.js.map +1 -1
  41. package/dist/backend/process/logger.d.ts +5 -3
  42. package/dist/backend/process/logger.d.ts.map +1 -1
  43. package/dist/backend/process/logger.js +26 -10
  44. package/dist/backend/process/logger.js.map +1 -1
  45. package/dist/backend/process/metrics-store.d.ts +6 -4
  46. package/dist/backend/process/metrics-store.d.ts.map +1 -1
  47. package/dist/backend/process/metrics-store.js +14 -12
  48. package/dist/backend/process/metrics-store.js.map +1 -1
  49. package/dist/backend/process/migrate.d.ts +12 -0
  50. package/dist/backend/process/migrate.d.ts.map +1 -0
  51. package/dist/backend/process/migrate.js +145 -0
  52. package/dist/backend/process/migrate.js.map +1 -0
  53. package/dist/backend/process/registry.d.ts +7 -3
  54. package/dist/backend/process/registry.d.ts.map +1 -1
  55. package/dist/backend/process/registry.js +13 -6
  56. package/dist/backend/process/registry.js.map +1 -1
  57. package/dist/backend/routes/chat.d.ts +2 -1
  58. package/dist/backend/routes/chat.d.ts.map +1 -1
  59. package/dist/backend/routes/chat.js +3 -3
  60. package/dist/backend/routes/chat.js.map +1 -1
  61. package/dist/backend/routes/process-routes.d.ts +3 -1
  62. package/dist/backend/routes/process-routes.d.ts.map +1 -1
  63. package/dist/backend/routes/process-routes.js +5 -5
  64. package/dist/backend/routes/process-routes.js.map +1 -1
  65. package/dist/backend/routes/upload.d.ts +4 -3
  66. package/dist/backend/routes/upload.d.ts.map +1 -1
  67. package/dist/backend/routes/upload.js +4 -3
  68. package/dist/backend/routes/upload.js.map +1 -1
  69. package/dist/cli/commands/data.d.ts +2 -3
  70. package/dist/cli/commands/data.d.ts.map +1 -1
  71. package/dist/cli/commands/data.js +20 -111
  72. package/dist/cli/commands/data.js.map +1 -1
  73. package/dist/cli/commands/eject.d.ts +12 -0
  74. package/dist/cli/commands/eject.d.ts.map +1 -0
  75. package/dist/cli/commands/eject.js +451 -0
  76. package/dist/cli/commands/eject.js.map +1 -0
  77. package/dist/cli/commands/generate-system-settings.d.ts +4 -4
  78. package/dist/cli/commands/generate-system-settings.d.ts.map +1 -1
  79. package/dist/cli/commands/generate-system-settings.js +147 -59
  80. package/dist/cli/commands/generate-system-settings.js.map +1 -1
  81. package/dist/cli/commands/init.d.ts.map +1 -1
  82. package/dist/cli/commands/init.js +45 -35
  83. package/dist/cli/commands/init.js.map +1 -1
  84. package/dist/cli/commands/observe.js +2 -2
  85. package/dist/cli/commands/observe.js.map +1 -1
  86. package/dist/cli/commands/process.js +2 -2
  87. package/dist/cli/commands/process.js.map +1 -1
  88. package/dist/cli/generators/capability-generator.d.ts.map +1 -1
  89. package/dist/cli/generators/capability-generator.js +8 -13
  90. package/dist/cli/generators/capability-generator.js.map +1 -1
  91. package/dist/cli/helpers/app-tsx-wiring.d.ts +8 -0
  92. package/dist/cli/helpers/app-tsx-wiring.d.ts.map +1 -1
  93. package/dist/cli/helpers/app-tsx-wiring.js +164 -0
  94. package/dist/cli/helpers/app-tsx-wiring.js.map +1 -1
  95. package/dist/cli/index.d.ts.map +1 -1
  96. package/dist/cli/index.js +2 -0
  97. package/dist/cli/index.js.map +1 -1
  98. package/dist/cli/templates/index.d.ts +3 -1
  99. package/dist/cli/templates/index.d.ts.map +1 -1
  100. package/dist/cli/templates/index.js +3 -1
  101. package/dist/cli/templates/index.js.map +1 -1
  102. package/dist/cli/templates/login-page.d.ts +4 -0
  103. package/dist/cli/templates/login-page.d.ts.map +1 -1
  104. package/dist/cli/templates/login-page.js +65 -3
  105. package/dist/cli/templates/login-page.js.map +1 -1
  106. package/dist/cli/templates/loom-config.d.ts.map +1 -1
  107. package/dist/cli/templates/loom-config.js +11 -13
  108. package/dist/cli/templates/loom-config.js.map +1 -1
  109. package/dist/cli/templates/notification-center-page.d.ts +9 -0
  110. package/dist/cli/templates/notification-center-page.d.ts.map +1 -0
  111. package/dist/cli/templates/notification-center-page.js +258 -0
  112. package/dist/cli/templates/notification-center-page.js.map +1 -0
  113. package/dist/cli/templates/notification-detail-page.d.ts +9 -0
  114. package/dist/cli/templates/notification-detail-page.d.ts.map +1 -0
  115. package/dist/cli/templates/notification-detail-page.js +102 -0
  116. package/dist/cli/templates/notification-detail-page.js.map +1 -0
  117. package/dist/config.d.ts +59 -59
  118. package/dist/config.d.ts.map +1 -1
  119. package/dist/config.js +22 -4
  120. package/dist/config.js.map +1 -1
  121. package/dist/types/config.d.ts +1 -1
  122. package/dist/types/config.d.ts.map +1 -1
  123. package/dist/types/process.d.ts +3 -3
  124. package/dist/types/process.d.ts.map +1 -1
  125. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"login-page.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/login-page.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,wBAAgB,iBAAiB,IAAI,MAAM,CAQ1C"}
1
+ {"version":3,"file":"login-page.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/login-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wBAAgB,iBAAiB,IAAI,MAAM,CAkE1C"}
@@ -1,12 +1,74 @@
1
1
  /**
2
2
  * Login page template
3
+ *
4
+ * Generates a standalone Login component as a local source file.
5
+ * Self-contained with per-page registerMessages() for i18n.
6
+ * Used by AuthGuard's fallback prop when ejected.
3
7
  */
4
8
  export function loginPageTemplate() {
5
- return `import React from 'react';
6
- import { LoginPage } from '@loom-framework/frontend-antd';
9
+ return `import React, { useState } from 'react';
10
+ import { Form, Input, Button, Card, message } from 'antd';
11
+ import { UserOutlined, LockOutlined } from '@ant-design/icons';
12
+ import { useAuth, useLocale, registerMessages } from '@loom-framework/frontend-antd';
13
+
14
+ registerMessages('zh-CN', {
15
+ 'auth.loginTitle': '登录',
16
+ 'auth.login': '登录',
17
+ 'auth.username': '用户名',
18
+ 'auth.password': '密码',
19
+ 'auth.usernameRequired': '请输入用户名',
20
+ 'auth.passwordRequired': '请输入密码',
21
+ 'auth.loginSuccess': '登录成功',
22
+ 'auth.loginFailed': '登录失败',
23
+ });
24
+
25
+ registerMessages('en-US', {
26
+ 'auth.loginTitle': 'Login',
27
+ 'auth.login': 'Login',
28
+ 'auth.username': 'Username',
29
+ 'auth.password': 'Password',
30
+ 'auth.usernameRequired': 'Username is required',
31
+ 'auth.passwordRequired': 'Password is required',
32
+ 'auth.loginSuccess': 'Login successful',
33
+ 'auth.loginFailed': 'Login failed',
34
+ });
7
35
 
8
36
  export default function Login() {
9
- return <LoginPage />;
37
+ const { login } = useAuth();
38
+ const { t } = useLocale();
39
+ const [loading, setLoading] = useState(false);
40
+
41
+ const handleSubmit = async (values: { username: string; password: string }) => {
42
+ setLoading(true);
43
+ try {
44
+ await login(values.username, values.password);
45
+ message.success(t('auth.loginSuccess'));
46
+ } catch (err) {
47
+ message.error(err instanceof Error ? err.message : t('auth.loginFailed'));
48
+ } finally {
49
+ setLoading(false);
50
+ }
51
+ };
52
+
53
+ return (
54
+ <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh', background: 'var(--ant-color-bg-layout)' }}>
55
+ <Card style={{ width: 400 }} title={t('auth.loginTitle')}>
56
+ <Form onFinish={handleSubmit} autoComplete="off" size="large">
57
+ <Form.Item name="username" rules={[{ required: true, message: t('auth.usernameRequired') }]}>
58
+ <Input prefix={<UserOutlined />} placeholder={t('auth.username')} />
59
+ </Form.Item>
60
+ <Form.Item name="password" rules={[{ required: true, message: t('auth.passwordRequired') }]}>
61
+ <Input.Password prefix={<LockOutlined />} placeholder={t('auth.password')} />
62
+ </Form.Item>
63
+ <Form.Item>
64
+ <Button type="primary" htmlType="submit" loading={loading} block>
65
+ {t('auth.login')}
66
+ </Button>
67
+ </Form.Item>
68
+ </Form>
69
+ </Card>
70
+ </div>
71
+ );
10
72
  }
11
73
  `;
12
74
  }
@@ -1 +1 @@
1
- {"version":3,"file":"login-page.js","sourceRoot":"","sources":["../../../src/cli/templates/login-page.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;;;;;;CAMR,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"login-page.js","sourceRoot":"","sources":["../../../src/cli/templates/login-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgER,CAAC;AACF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"loom-config.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/loom-config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,GAAG,QAAQ,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CA2D3E"}
1
+ {"version":3,"file":"loom-config.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/loom-config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,YAAY,GAAG,QAAQ,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAyD3E"}
@@ -44,19 +44,17 @@ export default defineConfig({
44
44
  theme: { defaultMode: 'light' },
45
45
  showHeader: true,
46
46
  },
47
- // Uncomment to enable authentication:
48
- // auth: {
49
- // provider: 'builtin',
50
- // secret: 'env:JWT_SECRET', // Read from environment variable
51
- // roles: [
52
- // { name: 'admin', permissions: [{ model: '*', level: 'admin' }] },
53
- // { name: 'editor', permissions: [{ model: '*', level: 'write' }] },
54
- // { name: 'viewer', permissions: [{ model: '*', level: 'read' }] },
55
- // ],
56
- // permissions: {
57
- // defaults: { read: 'read', write: 'write' },
58
- // },
59
- // },
47
+ auth: {
48
+ provider: 'builtin',
49
+ secret: 'env:LOOM_JWT_SECRET',
50
+ roles: [
51
+ { role: 'admin', permissions: [{ model: '*', level: 'admin' }] },
52
+ { role: 'user', permissions: [{ model: '*', level: 'write' }] },
53
+ ],
54
+ permissions: {
55
+ defaults: { read: 'read', write: 'write' },
56
+ },
57
+ },
60
58
  });
61
59
  `;
62
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"loom-config.js","sourceRoot":"","sources":["../../../src/cli/templates/loom-config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,MAAM,UAAU,gBAAgB,CAAC,OAAkC;IACjE,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,IAAI,SAAS,CAAC;IACpD,MAAM,SAAS,GAAG,OAAO,CAAC,gBAAgB,IAAI,EAAE,CAAC;IACjD,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;IAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC,yBAAyB,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,uBAAuB,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAEtE,OAAO;;;;aAII,OAAO,CAAC,IAAI;;+BAEM,OAAO,CAAC,WAAW,gBAAgB,OAAO,CAAC,IAAI;;;uBAGvD,OAAO,CAAC,OAAO;;;;;;;;;;;;;;;;;;mBAkBnB,SAAS,KAAK,aAAa,GAAG,WAAW;;;;;;;;;;;;;;;;;;;;;;;;CAwB3D,CAAC;AACF,CAAC"}
1
+ {"version":3,"file":"loom-config.js","sourceRoot":"","sources":["../../../src/cli/templates/loom-config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAWH,MAAM,UAAU,gBAAgB,CAAC,OAAkC;IACjE,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,IAAI,SAAS,CAAC;IACpD,MAAM,SAAS,GAAG,OAAO,CAAC,gBAAgB,IAAI,EAAE,CAAC;IACjD,MAAM,OAAO,GAAG,OAAO,CAAC,cAAc,IAAI,EAAE,CAAC;IAC7C,MAAM,aAAa,GAAG,SAAS,CAAC,CAAC,CAAC,yBAAyB,SAAS,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9E,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,uBAAuB,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAEtE,OAAO;;;;aAII,OAAO,CAAC,IAAI;;+BAEM,OAAO,CAAC,WAAW,gBAAgB,OAAO,CAAC,IAAI;;;uBAGvD,OAAO,CAAC,OAAO;;;;;;;;;;;;;;;;;;mBAkBnB,SAAS,KAAK,aAAa,GAAG,WAAW;;;;;;;;;;;;;;;;;;;;;;CAsB3D,CAAC;AACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * NotificationCenter page template
3
+ *
4
+ * Generates NotificationCenterPage as a local source file.
5
+ * Page imports sub-components from @loom-framework/frontend-antd and
6
+ * registers its own i18n keys via registerMessages().
7
+ */
8
+ export declare function notificationCenterPageTemplate(): string;
9
+ //# sourceMappingURL=notification-center-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-center-page.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/notification-center-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wBAAgB,8BAA8B,IAAI,MAAM,CAyPvD"}
@@ -0,0 +1,258 @@
1
+ /**
2
+ * NotificationCenter page template
3
+ *
4
+ * Generates NotificationCenterPage as a local source file.
5
+ * Page imports sub-components from @loom-framework/frontend-antd and
6
+ * registers its own i18n keys via registerMessages().
7
+ */
8
+ export function notificationCenterPageTemplate() {
9
+ return `import React, { useState, useCallback } from 'react';
10
+ import { Table, Button, Space, Tag, Typography, Popconfirm, Select, message, Breadcrumb, Flex } from 'antd';
11
+ import { CheckOutlined, DeleteOutlined, HomeOutlined } from '@ant-design/icons';
12
+ import { useNotificationCenter, useAppShell, useLocale, registerMessages } from '@loom-framework/frontend-antd';
13
+
14
+ registerMessages('zh-CN', {
15
+ 'notification.markRead': '已读',
16
+ 'notification.markAllRead': '全部已读',
17
+ 'notification.batchMarkRead': '批量已读',
18
+ 'notification.batchDelete': '批量删除',
19
+ 'notification.colType': '类型',
20
+ 'notification.colTitle': '标题',
21
+ 'notification.colDescription': '描述',
22
+ 'notification.colSource': '来源',
23
+ 'notification.backToList': '返回列表',
24
+ 'notification.notFound': '通知不存在或已被删除',
25
+ 'notification.clearAll': '清空所有',
26
+ 'notification.clearConfirm': '确认清空所有通知?',
27
+ 'notification.deleteConfirm': '确认删除该通知?',
28
+ 'notification.unread': '未读',
29
+ 'notification.all': '全部',
30
+ 'notification.type.info': '信息',
31
+ 'notification.type.warning': '警告',
32
+ 'notification.type.error': '错误',
33
+ 'notification.type.success': '成功',
34
+ 'notification.source.system': '系统',
35
+ 'notification.source.event': '事件',
36
+ 'notification.source.cli': '命令行',
37
+ });
38
+
39
+ registerMessages('en-US', {
40
+ 'notification.markRead': 'Mark read',
41
+ 'notification.markAllRead': 'Mark all read',
42
+ 'notification.batchMarkRead': 'Batch mark read',
43
+ 'notification.batchDelete': 'Batch delete',
44
+ 'notification.colType': 'Type',
45
+ 'notification.colTitle': 'Title',
46
+ 'notification.colDescription': 'Description',
47
+ 'notification.colSource': 'Source',
48
+ 'notification.backToList': 'Back to list',
49
+ 'notification.notFound': 'Notification not found or has been deleted',
50
+ 'notification.clearAll': 'Clear all',
51
+ 'notification.clearConfirm': 'Clear all notifications?',
52
+ 'notification.deleteConfirm': 'Delete this notification?',
53
+ 'notification.unread': 'Unread',
54
+ 'notification.all': 'All',
55
+ 'notification.type.info': 'Info',
56
+ 'notification.type.warning': 'Warning',
57
+ 'notification.type.error': 'Error',
58
+ 'notification.type.success': 'Success',
59
+ 'notification.source.system': 'System',
60
+ 'notification.source.event': 'Event',
61
+ 'notification.source.cli': 'CLI',
62
+ });
63
+
64
+ const NOTIFICATION_TYPE_COLORS: Record<string, string> = {
65
+ info: 'blue',
66
+ warning: 'orange',
67
+ error: 'red',
68
+ success: 'green',
69
+ };
70
+
71
+ const NOTIFICATION_SOURCE_COLORS: Record<string, string> = {
72
+ system: 'default',
73
+ event: 'purple',
74
+ cli: 'cyan',
75
+ };
76
+
77
+ export default function NotificationCenterPage() {
78
+ const { notifications, loading, fetchNotifications, markRead, markAllRead, deleteNotification, clearAll, markBatchRead, deleteBatch, selectNotification } = useNotificationCenter();
79
+ const { breadcrumbs, onNavClick } = useAppShell();
80
+ const { t } = useLocale();
81
+ const [filter, setFilter] = useState<string>('all');
82
+ const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
83
+
84
+ const handleFilterChange = useCallback((value: string) => {
85
+ setFilter(value);
86
+ if (value === 'unread') {
87
+ fetchNotifications({ limit: 50, unreadOnly: true });
88
+ } else {
89
+ fetchNotifications({ limit: 50 });
90
+ }
91
+ }, [fetchNotifications]);
92
+
93
+ const handleMarkAllRead = useCallback(async () => {
94
+ await markAllRead();
95
+ message.success(t('notification.markAllRead'));
96
+ }, [markAllRead, t]);
97
+
98
+ const handleBatchMarkRead = useCallback(async () => {
99
+ const ids = selectedRowKeys as string[];
100
+ await markBatchRead(ids);
101
+ setSelectedRowKeys([]);
102
+ message.success(t('notification.batchMarkRead'));
103
+ }, [markBatchRead, selectedRowKeys, t]);
104
+
105
+ const handleBatchDelete = useCallback(async () => {
106
+ const ids = selectedRowKeys as string[];
107
+ await deleteBatch(ids);
108
+ setSelectedRowKeys([]);
109
+ message.success(t('notification.batchDelete'));
110
+ }, [deleteBatch, selectedRowKeys, t]);
111
+
112
+ const handleClearAll = useCallback(async () => {
113
+ await clearAll();
114
+ setSelectedRowKeys([]);
115
+ message.success(t('notification.clearAll'));
116
+ }, [clearAll, t]);
117
+
118
+ const handleMarkRead = useCallback(async (id: string) => {
119
+ await markRead(id);
120
+ }, [markRead]);
121
+
122
+ const handleDelete = useCallback(async (id: string) => {
123
+ await deleteNotification(id);
124
+ }, [deleteNotification]);
125
+
126
+ const columns = [
127
+ {
128
+ title: t('notification.colType'),
129
+ dataIndex: 'type',
130
+ key: 'type',
131
+ width: 80,
132
+ render: (type: string) => <Tag color={NOTIFICATION_TYPE_COLORS[type] || 'default'}>{t(\`notification.type.\${type}\`)}</Tag>,
133
+ },
134
+ {
135
+ title: t('notification.colTitle'),
136
+ dataIndex: 'title',
137
+ key: 'title',
138
+ render: (title: string, record: { read: boolean }) => (
139
+ <Typography.Text strong={!record.read}>{title}</Typography.Text>
140
+ ),
141
+ },
142
+ {
143
+ title: t('notification.colDescription'),
144
+ dataIndex: 'description',
145
+ key: 'description',
146
+ ellipsis: true,
147
+ render: (desc: string) => (
148
+ <Typography.Text type="secondary" ellipsis>{desc}</Typography.Text>
149
+ ),
150
+ },
151
+ {
152
+ title: t('notification.colSource'),
153
+ dataIndex: 'source',
154
+ key: 'source',
155
+ width: 90,
156
+ render: (source: string) => <Tag color={NOTIFICATION_SOURCE_COLORS[source] || 'default'}>{t(\`notification.source.\${source}\`) || source}</Tag>,
157
+ },
158
+ {
159
+ title: t('field.createdAt'),
160
+ dataIndex: 'createdAt',
161
+ key: 'createdAt',
162
+ width: 140,
163
+ render: (val: string) => <Typography.Text type="secondary">{val ? new Date(val).toLocaleString() : '-'}</Typography.Text>,
164
+ },
165
+ {
166
+ title: t('common.action'),
167
+ key: 'action',
168
+ width: 100,
169
+ render: (_: unknown, record: { id: string; read: boolean }) => (
170
+ <Space size={0}>
171
+ {!record.read && (
172
+ <Button type="text" size="small" icon={<CheckOutlined />} onClick={(e) => { e.stopPropagation(); handleMarkRead(record.id); }}>
173
+ {t('notification.markRead')}
174
+ </Button>
175
+ )}
176
+ <Popconfirm
177
+ title={t('notification.deleteConfirm')}
178
+ onConfirm={() => handleDelete(record.id)}
179
+ >
180
+ <Button type="text" danger size="small" icon={<DeleteOutlined />} onClick={(e) => e.stopPropagation()} />
181
+ </Popconfirm>
182
+ </Space>
183
+ ),
184
+ },
185
+ ];
186
+
187
+ const filterOptions = [
188
+ { value: 'all', label: t('notification.all') },
189
+ { value: 'unread', label: t('notification.unread') },
190
+ { value: 'info', label: t('notification.type.info') },
191
+ { value: 'warning', label: t('notification.type.warning') },
192
+ { value: 'error', label: t('notification.type.error') },
193
+ { value: 'success', label: t('notification.type.success') },
194
+ ];
195
+
196
+ const filteredNotifications = filter === 'all' || filter === 'unread'
197
+ ? notifications
198
+ : notifications.filter(n => n.type === filter);
199
+
200
+ const hasSelected = selectedRowKeys.length > 0;
201
+
202
+ return (
203
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
204
+ <Flex justify="space-between" align="center" style={{ marginBottom: 12 }}>
205
+ <Breadcrumb items={[{ title: <HomeOutlined onClick={() => onNavClick?.('')} style={{ cursor: 'pointer' }} /> }, ...(breadcrumbs || []).map((b: { title: string; path?: string }) => ({ title: b.path ? <a onClick={() => { if (b.path) onNavClick?.(b.path); }}>{b.title}</a> : b.title }))]} />
206
+ <Space>
207
+ <Select
208
+ value={filter}
209
+ onChange={handleFilterChange}
210
+ options={filterOptions}
211
+ style={{ width: 120 }}
212
+ />
213
+ <Button icon={<CheckOutlined />} onClick={handleMarkAllRead}>{t('notification.markAllRead')}</Button>
214
+ <Popconfirm title={t('notification.clearConfirm')} onConfirm={handleClearAll}>
215
+ <Button danger>{t('notification.clearAll')}</Button>
216
+ </Popconfirm>
217
+ {hasSelected && (
218
+ <>
219
+ <Button icon={<CheckOutlined />} onClick={handleBatchMarkRead}>
220
+ {t('notification.batchMarkRead')} ({selectedRowKeys.length})
221
+ </Button>
222
+ <Popconfirm title={t('notification.deleteConfirm')} onConfirm={handleBatchDelete}>
223
+ <Button danger icon={<DeleteOutlined />}>
224
+ {t('notification.batchDelete')} ({selectedRowKeys.length})
225
+ </Button>
226
+ </Popconfirm>
227
+ </>
228
+ )}
229
+ </Space>
230
+ </Flex>
231
+ <div style={{ flex: 1, minHeight: 0, overflow: 'hidden' }}>
232
+ <Table
233
+ dataSource={filteredNotifications}
234
+ columns={columns}
235
+ rowKey="id"
236
+ loading={loading}
237
+ pagination={{ pageSize: 20, showTotal: (total: number) => t('crud.total', { total }) }}
238
+ size="middle"
239
+ scroll={{ x: 'max-content' }}
240
+ rowSelection={{
241
+ selectedRowKeys,
242
+ onChange: (keys) => setSelectedRowKeys(keys),
243
+ }}
244
+ onRow={(record) => ({
245
+ onClick: () => {
246
+ selectNotification(record.id);
247
+ onNavClick?.('notification-detail');
248
+ },
249
+ style: { cursor: 'pointer' },
250
+ })}
251
+ />
252
+ </div>
253
+ </div>
254
+ );
255
+ }
256
+ `;
257
+ }
258
+ //# sourceMappingURL=notification-center-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-center-page.js","sourceRoot":"","sources":["../../../src/cli/templates/notification-center-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,UAAU,8BAA8B;IAC5C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuPR,CAAC;AACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * NotificationDetail page template
3
+ *
4
+ * Generates NotificationDetailPage as a local source file.
5
+ * Page imports sub-components from @loom-framework/frontend-antd and
6
+ * registers its own i18n keys via registerMessages().
7
+ */
8
+ export declare function notificationDetailPageTemplate(): string;
9
+ //# sourceMappingURL=notification-detail-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-detail-page.d.ts","sourceRoot":"","sources":["../../../src/cli/templates/notification-detail-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,wBAAgB,8BAA8B,IAAI,MAAM,CA6FvD"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * NotificationDetail page template
3
+ *
4
+ * Generates NotificationDetailPage as a local source file.
5
+ * Page imports sub-components from @loom-framework/frontend-antd and
6
+ * registers its own i18n keys via registerMessages().
7
+ */
8
+ export function notificationDetailPageTemplate() {
9
+ return `import React from 'react';
10
+ import { Tag, Typography, Empty, Breadcrumb, Button, Space, Flex } from 'antd';
11
+ import { ArrowLeftOutlined, HomeOutlined } from '@ant-design/icons';
12
+ import { XMarkdown } from '@ant-design/x-markdown';
13
+ import '@ant-design/x-markdown/themes/light.css';
14
+ import '@ant-design/x-markdown/themes/dark.css';
15
+ import { useNotificationCenter, useAppShell, useLocale, useLoomTheme, registerMessages } from '@loom-framework/frontend-antd';
16
+
17
+ registerMessages('zh-CN', {
18
+ 'notification.backToList': '返回列表',
19
+ 'notification.notFound': '通知不存在或已被删除',
20
+ 'notification.type.info': '信息',
21
+ 'notification.type.warning': '警告',
22
+ 'notification.type.error': '错误',
23
+ 'notification.type.success': '成功',
24
+ 'notification.source.system': '系统',
25
+ 'notification.source.event': '事件',
26
+ 'notification.source.cli': '命令行',
27
+ });
28
+
29
+ registerMessages('en-US', {
30
+ 'notification.backToList': 'Back to list',
31
+ 'notification.notFound': 'Notification not found or has been deleted',
32
+ 'notification.type.info': 'Info',
33
+ 'notification.type.warning': 'Warning',
34
+ 'notification.type.error': 'Error',
35
+ 'notification.type.success': 'Success',
36
+ 'notification.source.system': 'System',
37
+ 'notification.source.event': 'Event',
38
+ 'notification.source.cli': 'CLI',
39
+ });
40
+
41
+ const NOTIFICATION_TYPE_COLORS: Record<string, string> = {
42
+ info: 'blue',
43
+ warning: 'orange',
44
+ error: 'red',
45
+ success: 'green',
46
+ };
47
+
48
+ const NOTIFICATION_SOURCE_COLORS: Record<string, string> = {
49
+ system: 'default',
50
+ event: 'purple',
51
+ cli: 'cyan',
52
+ };
53
+
54
+ export default function NotificationDetailPage() {
55
+ const { notifications, selectedNotificationId } = useNotificationCenter();
56
+ const { breadcrumbs, onNavClick } = useAppShell();
57
+ const { t } = useLocale();
58
+ const { mode } = useLoomTheme();
59
+
60
+ const notification = notifications.find(n => n.id === selectedNotificationId) ?? null;
61
+
62
+ const themeClass = mode === 'dark' ? 'x-markdown-dark' : 'x-markdown-light';
63
+
64
+ if (!notification) {
65
+ return (
66
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
67
+ <Empty description={t('notification.notFound')}>
68
+ <Button type="primary" onClick={() => onNavClick?.('notifications')}>
69
+ <ArrowLeftOutlined /> {t('notification.backToList')}
70
+ </Button>
71
+ </Empty>
72
+ </div>
73
+ );
74
+ }
75
+
76
+ return (
77
+ <div style={{ display: 'flex', flexDirection: 'column', flex: 1, minHeight: 0 }}>
78
+ <Flex justify="space-between" align="center" style={{ marginBottom: 12 }}>
79
+ <Breadcrumb items={[{ title: <HomeOutlined onClick={() => onNavClick?.('')} style={{ cursor: 'pointer' }} /> }, ...(breadcrumbs || []).map((b: { title: string; path?: string }) => ({ title: b.path ? <a onClick={() => { if (b.path) onNavClick?.(b.path); }}>{b.title}</a> : b.title }))]} />
80
+ <Button icon={<ArrowLeftOutlined />} onClick={() => onNavClick?.('notifications')}>
81
+ {t('notification.backToList')}
82
+ </Button>
83
+ </Flex>
84
+
85
+ <div style={{ marginBottom: 16 }}>
86
+ <Space size={8} align="center" style={{ marginBottom: 8 }}>
87
+ <Tag color={NOTIFICATION_TYPE_COLORS[notification.type]}>{t(\`notification.type.\${notification.type}\`)}</Tag>
88
+ <Tag color={NOTIFICATION_SOURCE_COLORS[notification.source] || 'default'}>{t(\`notification.source.\${notification.source}\`) || notification.source}</Tag>
89
+ <Typography.Text type="secondary">{notification.createdAt ? new Date(notification.createdAt).toLocaleString() : '-'}</Typography.Text>
90
+ </Space>
91
+ <Typography.Title level={4} style={{ marginBottom: 8 }}>{notification.title}</Typography.Title>
92
+ </div>
93
+
94
+ <div className={themeClass} style={{ flex: 1, overflow: 'auto' }}>
95
+ <XMarkdown content={notification.description || ''} />
96
+ </div>
97
+ </div>
98
+ );
99
+ }
100
+ `;
101
+ }
102
+ //# sourceMappingURL=notification-detail-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-detail-page.js","sourceRoot":"","sources":["../../../src/cli/templates/notification-detail-page.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,UAAU,8BAA8B;IAC5C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2FR,CAAC;AACF,CAAC"}