@allbluecn/web-app 0.19.5 → 0.21.0
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/package.json +3 -3
- package/src/components/bug/__tests__/board-subgroups.test.tsx +1 -1
- package/src/components/bug/__tests__/board-view.test.tsx +1 -1
- package/src/components/bug/__tests__/comment-reactions.test.tsx +1 -1
- package/src/components/bug/__tests__/create-bug-dialog.test.tsx +2 -2
- package/src/components/bug/__tests__/filters-compat.test.tsx +1 -1
- package/src/components/bug/__tests__/fix-request-race.test.tsx +1 -1
- package/src/components/bug/__tests__/peek-modes.test.tsx +1 -1
- package/src/components/bug/__tests__/use-paged-bug-groups.test.tsx +1 -1
- package/src/components/bug/__tests__/view-type.test.tsx +1 -1
- package/src/components/bug/bug-list-view.tsx +2 -2
- package/src/components/bug/bug-toolbar.tsx +1 -1
- package/src/components/bug/create-bug-dialog.tsx +3 -3
- package/src/components/bug/detail/__tests__/activity-timeline.test.tsx +1 -1
- package/src/components/bug/detail/__tests__/bug-detail-collab.test.tsx +1 -1
- package/src/components/bug/detail/__tests__/repro-report-card.test.tsx +1 -1
- package/src/components/bug/detail/activity-tabs.tsx +34 -3
- package/src/components/bug/detail/activity-timeline.tsx +1 -1
- package/src/components/bug/detail/bug-detail-body.tsx +3 -3
- package/src/components/bug/detail/comment-input.tsx +1 -1
- package/src/components/bug/detail/comment-item.tsx +1 -1
- package/src/components/bug/detail/comment-reactions.tsx +1 -1
- package/src/components/bug/detail/comment-thread.tsx +1 -1
- package/src/components/bug/detail/props-section.tsx +2 -2
- package/src/components/bug/detail/repro-report-card.tsx +1 -1
- package/src/components/bug/detail/reproduce-steps-view.tsx +1 -1
- package/src/components/bug/display/use-paged-bug-groups.ts +2 -2
- package/src/components/bug/filters/field-configs.tsx +1 -1
- package/src/components/bug/inline-editors/assignee-editor.tsx +1 -1
- package/src/components/bug/peek-panel.tsx +2 -2
- package/src/components/bug/reproduce-steps-editor.tsx +1 -1
- package/src/components/bug/story-bugs-panel.tsx +10 -6
- package/src/components/bug/views/board-view.tsx +2 -2
- package/src/components/bug/views/bug-list/batch-toolbar.tsx +1 -1
- package/src/components/bug/views/bug-list/group.tsx +1 -1
- package/src/components/bug/views/bug-list/index.tsx +1 -1
- package/src/components/bug/views/bug-list/inline-bug-creator.tsx +1 -1
- package/src/components/bug/views/bug-list/row.tsx +2 -2
- package/src/components/bug/views/create-view-dialog.tsx +1 -1
- package/src/components/design/editor/versions/hooks/use-design-versions.ts +11 -4
- package/src/components/design/editor/versions/page-version-panel.tsx +30 -11
- package/src/components/harness/code/workspace-context.tsx +5 -1
- package/src/components/harness/code/workspace-picker.tsx +19 -6
- package/src/components/harness/composer/harness-composer.tsx +25 -3
- package/src/components/harness/composer/mention/mention-resolvers.ts +2 -0
- package/src/components/harness/composer/mention/mention-suggestion-menu.tsx +6 -3
- package/src/components/harness/composer/mention/use-mention-sources.ts +1 -1
- package/src/components/iteration/iteration-progress-bar.tsx +2 -2
- package/src/components/knowledge/public/legal-compliance-editor.tsx +1 -1
- package/src/components/layout/sidebar-03/nav-knowledge.tsx +1 -1
- package/src/components/layout/sidebar-03/nav-workspace.tsx +1 -1
- package/src/components/project/invite-member-dialog.tsx +2 -2
- package/src/components/project/project-member-manager.tsx +0 -3
- package/src/components/shared-kit/list-kit/kanban/__tests__/instance-key.test.ts +1 -1
- package/src/components/story/__tests__/constants.test.ts +1 -1
- package/src/components/story/__tests__/dependency-type-badge.test.ts +1 -1
- package/src/components/story/__tests__/peek-panel-patch-sync.test.tsx +1 -1
- package/src/components/story/__tests__/story-list-view-peek-sync.test.tsx +1 -1
- package/src/components/story/ai/story-ai-panel.tsx +1 -1
- package/src/components/story/ai/story-ai-preview-list.tsx +2 -2
- package/src/components/story/ai/use-story-ai-model.ts +17 -4
- package/src/components/story/columns.tsx +2 -2
- package/src/components/story/create-story-dialog.tsx +4 -4
- package/src/components/story/detail/__tests__/activity-timeline.test.tsx +1 -1
- package/src/components/story/detail/__tests__/associated-attachments-panel.test.tsx +1 -1
- package/src/components/story/detail/__tests__/story-collab.test.tsx +1 -1
- package/src/components/story/detail/activity-tabs.tsx +1 -1
- package/src/components/story/detail/activity-timeline.tsx +36 -17
- package/src/components/story/detail/comment-input.tsx +1 -1
- package/src/components/story/detail/comment-item.tsx +1 -1
- package/src/components/story/detail/comment-thread.tsx +1 -1
- package/src/components/story/detail/reaction-bar.tsx +1 -1
- package/src/components/story/detail/story-detail-body.tsx +4 -4
- package/src/components/story/detail/story-detail-toolbar/__tests__/attachment-cache.test.ts +83 -0
- package/src/components/story/detail/story-detail-toolbar/associated-attachments-panel.tsx +50 -9
- package/src/components/story/detail/story-detail-toolbar/children.tsx +3 -3
- package/src/components/story/detail/story-detail-toolbar/dependency-panel.tsx +9 -8
- package/src/components/story/detail/story-detail-toolbar/index.tsx +8 -8
- package/src/components/story/detail/story-detail-toolbar/related-test-cases.tsx +2 -2
- package/src/components/story/detail/story-properties.tsx +2 -2
- package/src/components/story/display/__tests__/flatten-story-groups.test.ts +1 -1
- package/src/components/story/display/__tests__/group-fields.test.ts +1 -1
- package/src/components/story/display/__tests__/group-resolver.test.ts +2 -2
- package/src/components/story/display/__tests__/order-resolver.test.ts +1 -1
- package/src/components/story/display/__tests__/use-grouped-stories.test.ts +1 -1
- package/src/components/story/display/__tests__/use-paged-story-groups.test.ts +1 -1
- package/src/components/story/display/flatten-story-groups.ts +1 -1
- package/src/components/story/display/group-fields.ts +2 -2
- package/src/components/story/display/group-resolver.tsx +4 -4
- package/src/components/story/display/order-resolver.ts +3 -3
- package/src/components/story/display/types.ts +5 -4
- package/src/components/story/display/use-grouped-stories.ts +5 -5
- package/src/components/story/display/use-paged-story-groups.ts +11 -4
- package/src/components/story/entity-overlay.ts +24 -0
- package/src/components/story/filters/field-configs.tsx +2 -2
- package/src/components/story/inline-editors/assignee-editor.tsx +1 -1
- package/src/components/story/inline-editors/dependency-type-select.tsx +1 -1
- package/src/components/story/inline-editors/parent-story-editor.tsx +2 -2
- package/src/components/story/inline-editors/priority-editor.tsx +2 -2
- package/src/components/story/inline-editors/state-editor.tsx +2 -2
- package/src/components/story/inline-editors/story-select-dialog.tsx +2 -2
- package/src/components/story/peek-panel.tsx +6 -5
- package/src/components/story/priority-icon.tsx +2 -2
- package/src/components/story/relations/__tests__/attachment-manage-dialog.test.tsx +1 -1
- package/src/components/story/relations/__tests__/design-select-dialog.test.tsx +1 -1
- package/src/components/story/relations/__tests__/knowledge-select-dialog.test.tsx +1 -1
- package/src/components/story/relations/__tests__/link-manage-dialog.test.tsx +1 -1
- package/src/components/story/relations/attachment-manage-dialog.tsx +4 -4
- package/src/components/story/relations/design-select-dialog.tsx +2 -2
- package/src/components/story/relations/knowledge-select-dialog.tsx +2 -2
- package/src/components/story/relations/link-manage-dialog.tsx +2 -2
- package/src/components/story/relations/story-ref-row.tsx +3 -3
- package/src/components/story/story-list-table.tsx +1 -1
- package/src/components/story/story-list-view.tsx +1 -1
- package/src/components/story/story-toolbar.tsx +1 -1
- package/src/components/story/transfer-story-popover.tsx +3 -2
- package/src/components/story/views/__tests__/calendar-view.test.tsx +1 -1
- package/src/components/story/views/__tests__/kanban-drop-subgroup.test.ts +1 -1
- package/src/components/story/views/__tests__/kanban-peek-sync.test.tsx +2 -2
- package/src/components/story/views/__tests__/kanban-view-membership-sync.test.tsx +1 -1
- package/src/components/story/views/__tests__/timeline-view.test.tsx +1 -1
- package/src/components/story/views/calendar/__tests__/mappers.test.ts +1 -1
- package/src/components/story/views/calendar/mappers.ts +1 -1
- package/src/components/story/views/calendar-view.tsx +1 -1
- package/src/components/story/views/create-view-dialog.tsx +1 -1
- package/src/components/story/views/kanban-view.tsx +2 -2
- package/src/components/story/views/story-list/batch-preview-dialog.tsx +2 -2
- package/src/components/story/views/story-list/batch-toolbar.tsx +3 -3
- package/src/components/story/views/story-list/group.tsx +18 -5
- package/src/components/story/views/story-list/index.tsx +4 -2
- package/src/components/story/views/story-list/inline-story-creator.tsx +2 -2
- package/src/components/story/views/story-list/row.tsx +2 -2
- package/src/components/story/views/story-list/use-flat-rows.ts +1 -1
- package/src/components/story/views/timeline/__tests__/mappers.test.ts +2 -2
- package/src/components/story/views/timeline/mappers.ts +2 -2
- package/src/components/story/views/timeline/use-date-commit.ts +1 -1
- package/src/components/story/views/timeline-view.tsx +2 -2
- package/src/components/testcase/__tests__/case-row.test.tsx +1 -1
- package/src/components/testcase/__tests__/create-dialog-scope.test.tsx +1 -1
- package/src/components/testcase/__tests__/display-options.test.tsx +1 -1
- package/src/components/testcase/__tests__/export-entry.test.tsx +1 -1
- package/src/components/testcase/__tests__/list-view.test.tsx +1 -1
- package/src/components/testcase/__tests__/step-editor.test.tsx +4 -2
- package/src/components/testcase/__tests__/toolbar.test.tsx +1 -1
- package/src/components/testcase/create-testcase-dialog.tsx +6 -5
- package/src/components/testcase/detail/case-detail-body.tsx +1 -1
- package/src/components/testcase/detail/properties.tsx +1 -1
- package/src/components/testcase/display/display-dropdown.tsx +2 -2
- package/src/components/testcase/display/use-testcase-display-options.ts +1 -1
- package/src/components/testcase/filters/field-configs.tsx +1 -1
- package/src/components/testcase/generation/draft-card.tsx +2 -2
- package/src/components/testcase/inline-editors/assignee-editor.tsx +1 -1
- package/src/components/testcase/list/case-row.tsx +1 -1
- package/src/components/testcase/list/grouped-list.tsx +1 -1
- package/src/components/testcase/list-view.tsx +2 -2
- package/src/components/testcase/runs/convert-bug-button.tsx +2 -2
- package/src/components/testcase/step-editor.tsx +13 -2
- package/src/components/testcase/testcase-toolbar.tsx +2 -2
- package/src/components/testcase/views/batch-toolbar.tsx +1 -1
- package/src/components/testcase/views/board-view.tsx +2 -2
- package/src/lib/changelog/sdk-versions.ts +2 -2
- package/src/lib/collections/knowledge/knowledge-bases-collection.ts +2 -1
- package/src/lib/collections/query-keys.ts +18 -0
- package/src/lib/collections/reference/users-brief-collection.ts +2 -1
- package/src/lib/collections/reference/workspaces-collection.ts +2 -1
- package/src/lib/story/__tests__/activity-grouper.test.ts +1 -1
- package/src/lib/story/activity-grouper.ts +1 -1
- package/src/paraglide/messages/_index.d.ts +2 -0
- package/src/paraglide/messages/_index.js +88269 -5642
- package/src/paraglide/messages/en.js +28255 -0
- package/src/paraglide/messages/story_group_loadfailed1.d.ts +17 -0
- package/src/paraglide/messages/story_group_loadfailed1.js +30 -0
- package/src/paraglide/messages/story_group_retry.d.ts +16 -0
- package/src/paraglide/messages/story_group_retry.js +29 -0
- package/src/paraglide/messages/zh.js +28253 -0
- package/src/routes/_nav/bugs/$bugId/route.lazy.tsx +2 -2
- package/src/routes/_nav/projects/$projectId/iterations/$iterationId/route.lazy.tsx +1 -1
- package/src/routes/_nav/route.tsx +1 -2
- package/src/routes/_nav/stories/$storyId/route.lazy.tsx +2 -2
- package/src/routes/_nav/stories/$storyId/route.tsx +1 -1
- package/src/routes/api/design/prototype/__tests__/route.test.ts +4 -1
- package/src/server/__tests__/task-executor.test.ts +7 -0
- package/src/server/ai-gateway/__tests__/ai-output-guard.test.ts +4 -4
- package/src/server/ai-gateway/__tests__/tools-authorization.test.ts +25 -0
- package/src/server/ai-gateway/tools/server.ts +24 -15
- package/src/server/knowledge/reindex-guard.ts +35 -0
- package/src/server/lib/__tests__/cron-auth.test.ts +1 -1
- package/src/server/serverFns/__tests__/legal-code-canonical.test.ts +169 -0
- package/src/server/serverFns/__tests__/story-tree.test.ts +0 -1
- package/src/server/serverFns/__tests__/story-types.test.ts +2 -1
- package/src/server/serverFns/__tests__/update-bug-guard.test.ts +1 -1
- package/src/server/serverFns/bug/__tests__/bug-comments.test.ts +95 -44
- package/src/server/serverFns/bug/__tests__/bug-crud.test.ts +46 -18
- package/src/server/serverFns/bug/__tests__/bug-groups.test.ts +1 -1
- package/src/server/serverFns/bug/bug-activities.ts +2 -2
- package/src/server/serverFns/bug/bug-comments.ts +44 -19
- package/src/server/serverFns/bug/bug-crud/detail-select.ts +1 -1
- package/src/server/serverFns/bug/bug-crud/detail.ts +2 -2
- package/src/server/serverFns/bug/bug-crud.ts +142 -110
- package/src/server/serverFns/bug/bug-group-query.ts +1 -1
- package/src/server/serverFns/bug/bug-groups.ts +2 -2
- package/src/server/serverFns/bug/bug-story-utils.ts +1 -1
- package/src/server/serverFns/chat/conversations.ts +27 -13
- package/src/server/serverFns/harness/github-workspace.ts +32 -25
- package/src/server/serverFns/harness/search-mention-candidates.ts +3 -1
- package/src/server/serverFns/iteration/iteration-crud.ts +1 -1
- package/src/server/serverFns/knowledge/public/legal/create.ts +3 -0
- package/src/server/serverFns/knowledge/public/legal/update.ts +8 -8
- package/src/server/serverFns/knowledge-index.ts +12 -5
- package/src/server/serverFns/project.ts +5 -11
- package/src/server/serverFns/story/__tests__/story-dependencies.test.ts +15 -4
- package/src/server/serverFns/story/__tests__/story-transfer.test.ts +20 -0
- package/src/server/serverFns/story/link-preview.ts +3 -2
- package/src/server/serverFns/story/story-children.ts +1 -1
- package/src/server/serverFns/story/story-comments.ts +1 -1
- package/src/server/serverFns/story/story-crud/bulk.ts +1 -1
- package/src/server/serverFns/story/story-crud.ts +1 -1
- package/src/server/serverFns/story/story-dependencies.ts +61 -69
- package/src/server/serverFns/story/story-groups.ts +1 -1
- package/src/server/serverFns/story/story-transfer.ts +8 -20
- package/src/server/serverFns/testcase/__tests__/automation.test.ts +21 -0
- package/src/server/serverFns/testcase/__tests__/testcase-groups.test.ts +1 -1
- package/src/server/serverFns/testcase/automation.ts +24 -1
- package/src/server/serverFns/testcase/testcase-board.ts +1 -1
- package/src/server/serverFns/testcase/testcase-crud.ts +1 -1
- package/src/server/serverFns/testcase/testcase-display-options.ts +1 -1
- package/src/server/serverFns/testcase/testcase-filter-views.ts +1 -1
- package/src/server/serverFns/testcase/types.ts +1 -1
- package/src/server/story/types.ts +7 -3
- package/src/shared/bug/__tests__/constants.test.ts +1 -1
- package/src/{components → shared}/bug/types.ts +4 -3
- package/src/{components → shared}/story/constants.ts +1 -1
- package/src/{components → shared}/story/types.ts +1 -7
- package/src/types/project.ts +1 -2
- /package/src/{components/testcase/display → shared/testcase}/field-configs.ts +0 -0
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
17
|
|
|
18
|
-
import React, { useCallback, useEffect, useState } from "react";
|
|
18
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
19
19
|
import { m } from '@/paraglide/messages.js'
|
|
20
20
|
import { Button } from "@/components/ui/button";
|
|
21
21
|
import { Input } from "@/components/ui/input";
|
|
@@ -27,7 +27,7 @@ import { PageVersionTree, type VersionSnapshot } from "./page-version-tree";
|
|
|
27
27
|
import { RestoreVersionDialog } from "./restore-version-dialog";
|
|
28
28
|
import { Plus } from "lucide-react";
|
|
29
29
|
import { cn } from "@/lib/utils";
|
|
30
|
-
import { type
|
|
30
|
+
import { type DesignVersionFull } from "@allbluecn/kernel";
|
|
31
31
|
import { SUBTABS } from "./constants";
|
|
32
32
|
import { type PageVersionPanelProps, type PageVersion } from "./types";
|
|
33
33
|
import { compareDesignVersionDesc } from "./lib/semver";
|
|
@@ -120,6 +120,11 @@ export function PageVersionPanel({ designId, pageId }: PageVersionPanelProps) {
|
|
|
120
120
|
const [selectedPageVersionNo, setSelectedPageVersionNo] = useState<Map<string, number>>(new Map());
|
|
121
121
|
const [allPageVersionsByPage, setAllPageVersionsByPage] = useState<Map<string, { version: number; notes: string | null; createdAt: string }[]>>(new Map());
|
|
122
122
|
|
|
123
|
+
// S31(审计 A7x-7 ⚪ 勾选风暴治理):版本选项 per-page 缓存——勾选变化只拉
|
|
124
|
+
// 增量新页面,已加载页不再重复请求。失效策略:designId 切换全量失效;发布
|
|
125
|
+
// 成功后由 executePublish 清缓存(新版本产生使已缓存列表失真)。
|
|
126
|
+
const pageVersionsCacheRef = useRef<{ designId: string; map: Map<string, PageVersion[]> }>({ designId: "", map: new Map() });
|
|
127
|
+
|
|
123
128
|
const [confirmUpdateOpen, setConfirmUpdateOpen] = useState(false);
|
|
124
129
|
const [confirmSkipOpen, setConfirmSkipOpen] = useState(false);
|
|
125
130
|
const [skipVersionInfo, setSkipVersionInfo] = useState<{ current: string; previous: string } | null>(null);
|
|
@@ -130,8 +135,8 @@ export function PageVersionPanel({ designId, pageId }: PageVersionPanelProps) {
|
|
|
130
135
|
selectedPageVersionsObj[pid] = versionNo;
|
|
131
136
|
}
|
|
132
137
|
try {
|
|
133
|
-
//
|
|
134
|
-
//
|
|
138
|
+
// S32:kernel DesignVersionFull 即返回行契约;pageVersions 为 Json 列,
|
|
139
|
+
// JsonValue→结构化形状不可比较(TS 限制),边界断言收敛于此
|
|
135
140
|
const version = (await createDesignVersionFn({
|
|
136
141
|
data: {
|
|
137
142
|
designId,
|
|
@@ -142,8 +147,10 @@ export function PageVersionPanel({ designId, pageId }: PageVersionPanelProps) {
|
|
|
142
147
|
pageIds: Array.from(selectedPageIds),
|
|
143
148
|
selectedPageVersions: selectedPageVersionsObj,
|
|
144
149
|
},
|
|
145
|
-
})) as unknown as
|
|
150
|
+
})) as unknown as DesignVersionFull;
|
|
146
151
|
upsertVersion(version);
|
|
152
|
+
// S31:发布产生新版本,受影响页面的版本选项缓存失效
|
|
153
|
+
pageVersionsCacheRef.current.map.clear();
|
|
147
154
|
setNotes("");
|
|
148
155
|
} catch (err) {
|
|
149
156
|
console.error(err instanceof Error ? err.message : "unknown");
|
|
@@ -188,24 +195,36 @@ export function PageVersionPanel({ designId, pageId }: PageVersionPanelProps) {
|
|
|
188
195
|
// Fetch page version options for all leaf pages when publish tab is active
|
|
189
196
|
useEffect(() => {
|
|
190
197
|
if (!isPublishTab) return;
|
|
198
|
+
const cache = pageVersionsCacheRef.current;
|
|
199
|
+
if (cache.designId !== designId) {
|
|
200
|
+
cache.designId = designId;
|
|
201
|
+
cache.map = new Map();
|
|
202
|
+
}
|
|
191
203
|
const allPageIds = getAllPageIds(tree);
|
|
192
204
|
const leafIds = allPageIds.filter((id) => selectedPageIds.has(id));
|
|
193
205
|
if (leafIds.length === 0) return;
|
|
206
|
+
// S31:只拉缓存未命中的页面(勾选风暴治理——勾选增量不再全量重拉)
|
|
207
|
+
const uncachedIds = leafIds.filter((id) => !cache.map.has(id));
|
|
208
|
+
if (uncachedIds.length === 0) {
|
|
209
|
+
setAllPageVersionsByPage(new Map(leafIds.map((id) => [id, cache.map.get(id) ?? []])));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
194
212
|
let cancelled = false; // 过期响应守卫:快速变更选择时旧批次不得覆盖
|
|
195
213
|
const fetchVersions = async () => {
|
|
196
214
|
try {
|
|
197
215
|
const results = await Promise.all(
|
|
198
|
-
|
|
216
|
+
uncachedIds.map(async (pid) => {
|
|
199
217
|
const versions = await listDesignPageVersionsFn({ data: { pageId: pid, limit: 100 } });
|
|
200
218
|
return { pageId: pid, versions };
|
|
201
219
|
})
|
|
202
220
|
);
|
|
203
221
|
if (cancelled) return;
|
|
204
|
-
const map = new Map<string, PageVersion[]>();
|
|
205
222
|
for (const r of results) {
|
|
206
|
-
if (r)
|
|
223
|
+
if (r) {
|
|
224
|
+
cache.map.set(r.pageId, r.versions.map((v) => ({ ...v, createdAt: v.createdAt.toISOString() })));
|
|
225
|
+
}
|
|
207
226
|
}
|
|
208
|
-
setAllPageVersionsByPage(map);
|
|
227
|
+
setAllPageVersionsByPage(new Map(leafIds.map((id) => [id, cache.map.get(id) ?? []])));
|
|
209
228
|
setSelectedPageVersionNo((prev) => {
|
|
210
229
|
const next = new Map(prev);
|
|
211
230
|
for (const r of results) {
|
|
@@ -223,7 +242,7 @@ export function PageVersionPanel({ designId, pageId }: PageVersionPanelProps) {
|
|
|
223
242
|
};
|
|
224
243
|
fetchVersions();
|
|
225
244
|
return () => { cancelled = true; };
|
|
226
|
-
}, [isPublishTab, selectedPageIds, tree]);
|
|
245
|
+
}, [isPublishTab, selectedPageIds, tree, designId]);
|
|
227
246
|
|
|
228
247
|
return (
|
|
229
248
|
<div className="flex flex-col h-full">
|
|
@@ -398,7 +417,7 @@ export function PageVersionPanel({ designId, pageId }: PageVersionPanelProps) {
|
|
|
398
417
|
</div>
|
|
399
418
|
) : (
|
|
400
419
|
<PageVersionTree
|
|
401
|
-
versions={versions
|
|
420
|
+
versions={versions}
|
|
402
421
|
onRestore={async (snapshot: VersionSnapshot) => {
|
|
403
422
|
let targetCanvas = snapshot.canvas;
|
|
404
423
|
let targetContent = snapshot.content;
|
|
@@ -18,9 +18,13 @@
|
|
|
18
18
|
|
|
19
19
|
import { createContext, useContext, useState, type ReactNode } from "react"
|
|
20
20
|
|
|
21
|
+
// S27:WorkspaceSource 单一权威定义(此前 workspace-picker 另有一份同名异构:
|
|
22
|
+
// local-fsap 丢 dirHandle、缺 local/cloud 变体,经 onChange 流入 context 即残缺)。
|
|
23
|
+
// FSAP 的 dirHandle 仅存客户端(不可序列化),path 为目录展示名。
|
|
21
24
|
export type WorkspaceSource =
|
|
22
25
|
| { type: "cloud"; id: string }
|
|
23
|
-
| { type: "local
|
|
26
|
+
| { type: "local"; path: string } // Tauri 本地路径
|
|
27
|
+
| { type: "local-fsap"; dirHandle: FileSystemDirectoryHandle; path: string }
|
|
24
28
|
| { type: "github"; owner: string; repo: string; branch: string }
|
|
25
29
|
|
|
26
30
|
interface WorkspaceContextValue {
|
|
@@ -26,17 +26,16 @@ import {
|
|
|
26
26
|
SelectTrigger,
|
|
27
27
|
SelectValue,
|
|
28
28
|
} from "@/components/ui/select"
|
|
29
|
-
import { listGithubReposFn, listGithubBranchesFn } from "@/server/serverFns/harness/github-workspace"
|
|
29
|
+
import { listGithubReposFn, listGithubBranchesFn, getGithubConnectionFn } from "@/server/serverFns/harness/github-workspace"
|
|
30
30
|
import { useQuery } from "@tanstack/react-query"
|
|
31
31
|
import { m } from "@/paraglide/messages.js"
|
|
32
32
|
import { isDesktop, getTauriInvoke } from "@/lib/desktop-detect"
|
|
33
33
|
import { toast } from "@/components/ui/toast"
|
|
34
34
|
import { hasFileSystemAccess, pickLocalDirectory } from "@/lib/fsap"
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
| { type: "github"; owner: string; repo: string; branch: string }
|
|
36
|
+
// S27:类型从 workspace-context 单一来源导入(删除同名异构副本)
|
|
37
|
+
import type { WorkspaceSource } from "./workspace-context";
|
|
38
|
+
|
|
40
39
|
|
|
41
40
|
interface WorkspacePickerProps {
|
|
42
41
|
value?: WorkspaceSource
|
|
@@ -64,6 +63,17 @@ export function WorkspacePicker({ value, onChange }: WorkspacePickerProps) {
|
|
|
64
63
|
enabled: mode === "github",
|
|
65
64
|
})
|
|
66
65
|
|
|
66
|
+
// S27:连接状态显式取自 serverFn(token 不出服务端),未连接给可操作提示而非空列表
|
|
67
|
+
const { data: connection } = useQuery({
|
|
68
|
+
queryKey: ["github-connection"],
|
|
69
|
+
queryFn: async () => {
|
|
70
|
+
const result = await getGithubConnectionFn()
|
|
71
|
+
return result as { connected: boolean; source: "saved" | "session" | null }
|
|
72
|
+
},
|
|
73
|
+
enabled: mode === "github",
|
|
74
|
+
staleTime: 30_000,
|
|
75
|
+
})
|
|
76
|
+
|
|
67
77
|
const selectedRepo = repos?.repos.find((r) => {
|
|
68
78
|
if (value?.type !== "github") return false
|
|
69
79
|
return r.owner === value.owner && r.name === value.repo
|
|
@@ -95,7 +105,8 @@ export function WorkspacePicker({ value, onChange }: WorkspacePickerProps) {
|
|
|
95
105
|
} else if (hasFileSystemAccess()) {
|
|
96
106
|
const handle = await pickLocalDirectory()
|
|
97
107
|
if (handle) {
|
|
98
|
-
|
|
108
|
+
// S27:dirHandle 必须随选入 context(此前仅传 name,文件树拿到 undefined 句柄)
|
|
109
|
+
onChange({ type: "local-fsap", dirHandle: handle, path: handle.name })
|
|
99
110
|
}
|
|
100
111
|
} else {
|
|
101
112
|
// Firefox/Safari 降级:提示上传到云工作区
|
|
@@ -167,6 +178,8 @@ export function WorkspacePicker({ value, onChange }: WorkspacePickerProps) {
|
|
|
167
178
|
<label className="text-sm font-medium">{m.chat_workspace_selectRepo()}</label>
|
|
168
179
|
{loadingRepos ? (
|
|
169
180
|
<Loader2 className="size-4 animate-spin" />
|
|
181
|
+
) : connection && !connection.connected ? (
|
|
182
|
+
<p className="text-xs text-muted-foreground">{m.chat_workspace_githubNotConnected()}</p>
|
|
170
183
|
) : (
|
|
171
184
|
<Select
|
|
172
185
|
value={selectedRepo?.full_name}
|
|
@@ -48,6 +48,7 @@ export interface MentionCandidate {
|
|
|
48
48
|
label: string
|
|
49
49
|
id: string
|
|
50
50
|
kbId?: string
|
|
51
|
+
kbName?: string
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export interface PrefillMentionEntity {
|
|
@@ -241,7 +242,7 @@ export function HarnessComposer({
|
|
|
241
242
|
})
|
|
242
243
|
|
|
243
244
|
const handleSend = useCallback(
|
|
244
|
-
(text: string) => {
|
|
245
|
+
(text: string, whenBusy: "queue" | "interrupt" = "queue") => {
|
|
245
246
|
if (!text.trim() || !hasAiKey) return
|
|
246
247
|
|
|
247
248
|
if (mediaRoute === "spec") {
|
|
@@ -277,7 +278,7 @@ export function HarnessComposer({
|
|
|
277
278
|
const unsentNonImage = attachments.some((a) => !(a.status === "ready" && a.dataUrl))
|
|
278
279
|
|
|
279
280
|
onSend(text, {
|
|
280
|
-
whenBusy
|
|
281
|
+
whenBusy,
|
|
281
282
|
knowledgeBaseIds: mergedKbIds.length ? mergedKbIds : undefined,
|
|
282
283
|
attachments: readyImages.length ? readyImages : undefined,
|
|
283
284
|
// 透传板块上下文(如果存在),服务端可据此注入系统提示词
|
|
@@ -336,9 +337,30 @@ export function HarnessComposer({
|
|
|
336
337
|
className="px-3 pt-3"
|
|
337
338
|
onKeyDownCapture={(event) => {
|
|
338
339
|
if (event.key !== "Enter" || event.shiftKey || event.nativeEvent.isComposing) return
|
|
340
|
+
// IME 三保险(与 useComposerSubmit 同口径):Safari compositionend 后窗口 + keyCode 229 + 按住不连发
|
|
341
|
+
if (event.keyCode === 229) return
|
|
342
|
+
if (event.repeat) return
|
|
339
343
|
if (event.target instanceof HTMLElement && !event.target.isContentEditable && event.target.getAttribute("contenteditable") !== "true") return
|
|
344
|
+
// S30(审计 A7x-8 待决策):修饰键进渲染期决策机——busy 时 Ctrl/Cmd+Enter
|
|
345
|
+
// 翻转 queue/steer 偏好提交;steerQueue 分支(空文本打断重排)待上游
|
|
346
|
+
// steer API(composer props 尚无),暂按 blocked 静默
|
|
347
|
+
const decision = decideSubmit({
|
|
348
|
+
text: editorRef.current?.getText() ?? "",
|
|
349
|
+
hasUploadingAttachments: hasUploading,
|
|
350
|
+
isRunning,
|
|
351
|
+
busyPreference,
|
|
352
|
+
modifierKey: event.metaKey || event.ctrlKey,
|
|
353
|
+
composerLocked: false,
|
|
354
|
+
})
|
|
355
|
+
if (decision.action === "blocked" || decision.action === "steerQueue") {
|
|
356
|
+
event.preventDefault()
|
|
357
|
+
return
|
|
358
|
+
}
|
|
340
359
|
event.preventDefault()
|
|
341
|
-
handleSend(
|
|
360
|
+
handleSend(
|
|
361
|
+
editorRef.current?.getText() ?? "",
|
|
362
|
+
decision.action === "steer" ? "interrupt" : decision.action === "queue" ? "queue" : "queue",
|
|
363
|
+
)
|
|
342
364
|
}}
|
|
343
365
|
>
|
|
344
366
|
<ComposerEditor
|
|
@@ -79,9 +79,12 @@ export function MentionSuggestionMenu(props: MentionSuggestionMenuProps) {
|
|
|
79
79
|
.map((item) => ({
|
|
80
80
|
title: item.label,
|
|
81
81
|
context: { kind: item.kind, id: item.id, label: item.label, kbId: item.kbId },
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
group:
|
|
82
|
+
// 分组:kb-doc 按所属知识库名(S29),prd 用稳定 kind 语义标签;
|
|
83
|
+
// 无名兜底(存量会话/异常数据)回落 kind 标签
|
|
84
|
+
group:
|
|
85
|
+
item.kind === "kb-doc"
|
|
86
|
+
? (item.kbName || m.harness_mention_kb_doc())
|
|
87
|
+
: m.harness_mention_prd(),
|
|
85
88
|
onSelect: handleSelect,
|
|
86
89
|
}))
|
|
87
90
|
}
|
|
@@ -35,7 +35,7 @@ export function useMentionSources() {
|
|
|
35
35
|
|
|
36
36
|
return useMemo(() => {
|
|
37
37
|
const all: MentionCandidate[] = [
|
|
38
|
-
...kbDocs.map((d) => ({ kind: "kb-doc" as const, id: d.id, label: d.label, kbId: d.kbId })),
|
|
38
|
+
...kbDocs.map((d) => ({ kind: "kb-doc" as const, id: d.id, label: d.label, kbId: d.kbId, kbName: d.kbName })),
|
|
39
39
|
...prds.map((p) => ({ kind: "design" as const, id: p.id, label: p.title })),
|
|
40
40
|
]
|
|
41
41
|
// 透传加载态:首载/失败不再被硬编码 false 吞掉,mention 菜单可区分加载中
|
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
//
|
|
15
15
|
// You should have received a copy of the GNU Affero General Public License
|
|
16
16
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
17
|
-
import { STATE_COLOR } from "@/
|
|
18
|
-
import type { StoryState } from "@/
|
|
17
|
+
import { STATE_COLOR } from "@/shared/story/constants";
|
|
18
|
+
import type { StoryState } from "@/shared/story/constants";
|
|
19
19
|
import { m } from "@/paraglide/messages.js";
|
|
20
20
|
|
|
21
21
|
const ORDER: StoryState[] = ["backlog", "todo", "in_progress", "done", "cancelled"];
|
|
@@ -87,7 +87,7 @@ function toDateStr(d: unknown): string {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
function mapRecordToProperties(record: LegalOrComplianceDetail, type: EditorType): PropertyValues {
|
|
90
|
-
//
|
|
90
|
+
// legal 的 code 已持久化(S7,detail 整行返回直接带出);compliance 仍无列,空串展示
|
|
91
91
|
if (type === 'legal') {
|
|
92
92
|
const r = record as LegalDetail & { code?: string }
|
|
93
93
|
return {
|
|
@@ -70,7 +70,7 @@ import { cn } from "@/lib/utils";
|
|
|
70
70
|
import { isParentActive } from "@/lib/sidebar-utils";
|
|
71
71
|
import { useEffect, useState } from "react";
|
|
72
72
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
73
|
-
import { KNOWLEDGE_BASES_QUERY_KEY } from "@/lib/collections/
|
|
73
|
+
import { KNOWLEDGE_BASES_QUERY_KEY } from "@/lib/collections/query-keys";
|
|
74
74
|
import {
|
|
75
75
|
listKnowledgeFn,
|
|
76
76
|
toggleKnowledgePinFn,
|
|
@@ -31,7 +31,7 @@ import { cn } from "@/lib/utils";
|
|
|
31
31
|
import { isParentActive } from "@/lib/sidebar-utils";
|
|
32
32
|
import { LucideIconByName } from "@/components/shared-kit/icons/lucide-icon-picker/lucide-icon-by-name";
|
|
33
33
|
import { CreateWorkspaceDialog } from "@/components/workspace/dialogs/create-workspace-dialog";
|
|
34
|
-
import { WORKSPACES_QUERY_KEY } from "@/lib/collections/
|
|
34
|
+
import { WORKSPACES_QUERY_KEY } from "@/lib/collections/query-keys";
|
|
35
35
|
import {
|
|
36
36
|
listPinnedWorkspacesBriefFn,
|
|
37
37
|
toggleWorkspacePinFn,
|
|
@@ -95,9 +95,9 @@ export function InviteMemberDialog({
|
|
|
95
95
|
},
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
+
// S41 最小披露:项目成员搜索不按 email 过滤(team 管理域不受此限)
|
|
98
99
|
const filteredMembers = members.filter((m) =>
|
|
99
|
-
m.username.toLowerCase().includes(searchQuery.toLowerCase())
|
|
100
|
-
m.email?.toLowerCase().includes(searchQuery.toLowerCase())
|
|
100
|
+
m.username.toLowerCase().includes(searchQuery.toLowerCase())
|
|
101
101
|
);
|
|
102
102
|
|
|
103
103
|
const handleSubmit = async () => {
|
|
@@ -157,9 +157,6 @@ export function ProjectMemberManager({
|
|
|
157
157
|
</Badge>
|
|
158
158
|
)}
|
|
159
159
|
</div>
|
|
160
|
-
<p className="truncate text-sm text-muted-foreground">
|
|
161
|
-
{member.user?.email}
|
|
162
|
-
</p>
|
|
163
160
|
</div>
|
|
164
161
|
<span className="hidden text-xs text-muted-foreground sm:block">
|
|
165
162
|
{new Intl.DateTimeFormat("zh-CN", { year: "numeric", month: "long", day: "numeric" }).format(new Date(member.joinedAt))}
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
itemKey,
|
|
24
24
|
storyIdFromInstanceKey,
|
|
25
25
|
} from "../instance-key";
|
|
26
|
-
import type { StoryListItem } from "
|
|
26
|
+
import type { StoryListItem } from "@/shared/story/types";
|
|
27
27
|
|
|
28
28
|
function story(id: string): StoryListItem {
|
|
29
29
|
return { id } as StoryListItem;
|
|
@@ -91,7 +91,7 @@ vi.mock("../detail/story-detail-body", () => ({
|
|
|
91
91
|
}));
|
|
92
92
|
|
|
93
93
|
import { StoryPeekPanel } from "../peek-panel";
|
|
94
|
-
import type { StoryDetail } from "
|
|
94
|
+
import type { StoryDetail } from "@/shared/story/types";
|
|
95
95
|
|
|
96
96
|
function detail(priority: StoryDetail["priority"]): StoryDetail {
|
|
97
97
|
return {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
22
22
|
import { act, render, waitFor } from "@testing-library/react";
|
|
23
23
|
import React from "react";
|
|
24
|
-
import type { StoryListItem, UserBrief } from "
|
|
24
|
+
import type { StoryListItem, UserBrief } from "@/shared/story/types";
|
|
25
25
|
|
|
26
26
|
const mocks = vi.hoisted(() => ({
|
|
27
27
|
onViewChange: undefined as undefined | ((v: string) => void),
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
import { parseJsonlBuffer, parseFinalBuffer } from "@/lib/ai/story-jsonl"
|
|
40
40
|
import { StoryAiPreviewList, type PreviewStoryItem } from "./story-ai-preview-list"
|
|
41
41
|
import { StorySearchPopover } from "@/components/story/search-popover"
|
|
42
|
-
import type { UserBrief } from "@/
|
|
42
|
+
import type { UserBrief } from "@/shared/story/types"
|
|
43
43
|
import { m } from "@/paraglide/messages.js";
|
|
44
44
|
|
|
45
45
|
type AiTab = "create" | "split" | "import"
|
|
@@ -26,8 +26,8 @@ import {
|
|
|
26
26
|
SelectTrigger,
|
|
27
27
|
SelectValue,
|
|
28
28
|
} from "@/components/ui/select"
|
|
29
|
-
import { STORY_PRIORITIES } from "@/
|
|
30
|
-
import type { UserBrief } from "@/
|
|
29
|
+
import { STORY_PRIORITIES } from "@/shared/story/constants"
|
|
30
|
+
import type { UserBrief } from "@/shared/story/types"
|
|
31
31
|
import { m } from "@/paraglide/messages.js";
|
|
32
32
|
|
|
33
33
|
const PRIORITY_LABELS: Record<string, () => string> = {
|
|
@@ -22,6 +22,17 @@ import { useCallback, useEffect, useMemo, useState } from "react"
|
|
|
22
22
|
|
|
23
23
|
const STORAGE_KEY = "story:descriptionAiModel"
|
|
24
24
|
|
|
25
|
+
// 浏览器存储访问必须 try/catch:本项目 SSR 的 Node 环境存在注入的 window
|
|
26
|
+
// (opaque origin,localStorage 访问抛 SecurityError),typeof 守卫会穿透
|
|
27
|
+
// (S33,与 lib/ai/use-selected-model readStorage 同口径)
|
|
28
|
+
function readStoredModel(): string {
|
|
29
|
+
try {
|
|
30
|
+
return window.localStorage.getItem(STORAGE_KEY) ?? ""
|
|
31
|
+
} catch {
|
|
32
|
+
return ""
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
export interface StoryAiModelGroup {
|
|
26
37
|
label: string
|
|
27
38
|
items: { value: string; label: string }[]
|
|
@@ -57,8 +68,7 @@ export function useStoryAiModel(): {
|
|
|
57
68
|
const defaultModel = session?.user?.aiModel ?? "openai/gpt-4o-mini"
|
|
58
69
|
const [model, setModel] = useState<string>(
|
|
59
70
|
() =>
|
|
60
|
-
(
|
|
61
|
-
"",
|
|
71
|
+
readStoredModel(),
|
|
62
72
|
)
|
|
63
73
|
|
|
64
74
|
// Fetch enabled models from server
|
|
@@ -106,8 +116,11 @@ export function useStoryAiModel(): {
|
|
|
106
116
|
|
|
107
117
|
// Sync localStorage
|
|
108
118
|
useEffect(() => {
|
|
109
|
-
if (
|
|
119
|
+
if (!model) return
|
|
120
|
+
try {
|
|
110
121
|
localStorage.setItem(STORAGE_KEY, model)
|
|
122
|
+
} catch {
|
|
123
|
+
// SSR 注入 window / 隐私模式:持久化失败可接受,仅内存态生效
|
|
111
124
|
}
|
|
112
125
|
}, [model])
|
|
113
126
|
|
|
@@ -118,7 +131,7 @@ export function useStoryAiModel(): {
|
|
|
118
131
|
const modelValues = useMemo(() => rawModels.map((m) => m.model), [rawModels])
|
|
119
132
|
useEffect(() => {
|
|
120
133
|
if (!session || modelValues.length === 0) return
|
|
121
|
-
const stored =
|
|
134
|
+
const stored = readStoredModel()
|
|
122
135
|
if (stored && modelValues.includes(stored)) {
|
|
123
136
|
if (model !== stored) setModel(stored)
|
|
124
137
|
return
|
|
@@ -28,8 +28,8 @@ import {
|
|
|
28
28
|
import { Badge } from "@/components/ui/badge";
|
|
29
29
|
import { UserAvatar } from "@/components/shared-kit";
|
|
30
30
|
import { PriorityIcon } from "./priority-icon";
|
|
31
|
-
import { STATE_COLOR, PRIORITY_RANK } from "
|
|
32
|
-
import type { StoryListItem } from "
|
|
31
|
+
import { STATE_COLOR, PRIORITY_RANK } from "@/shared/story/constants";
|
|
32
|
+
import type { StoryListItem } from "@/shared/story/types";
|
|
33
33
|
import { m } from '@/paraglide/messages.js'
|
|
34
34
|
|
|
35
35
|
const STATE_LABEL_FNS: Record<string, () => string> = {
|
|
@@ -38,9 +38,9 @@ import {
|
|
|
38
38
|
|
|
39
39
|
import { createStoryFn } from "@/server/serverFns/story"
|
|
40
40
|
import { ensureBuiltinProjectFn } from "@/server/serverFns/project"
|
|
41
|
-
import type { StoryPriority, StoryState } from "
|
|
42
|
-
import type { UserBrief } from "
|
|
43
|
-
import { serializeStoryDescription } from "
|
|
41
|
+
import type { StoryPriority, StoryState } from "@/shared/story/constants"
|
|
42
|
+
import type { UserBrief } from "@/shared/story/types"
|
|
43
|
+
import { serializeStoryDescription } from "@/shared/story/types"
|
|
44
44
|
import { StateEditor, PriorityEditor, AssigneeEditor, DateEditor, LabelsEditor } from "./inline-editors"
|
|
45
45
|
import { createStorySchema } from "./story-create-schema"
|
|
46
46
|
|
|
@@ -62,7 +62,7 @@ interface CreateStoryDialogProps {
|
|
|
62
62
|
initialProjectId?: string
|
|
63
63
|
onSuccess?: (
|
|
64
64
|
story: { id: string; name: string; identifier?: string },
|
|
65
|
-
activities?: import("
|
|
65
|
+
activities?: import("@/shared/story/types").ActivityItem[],
|
|
66
66
|
) => void
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { describe, it, expect, vi } from "vitest";
|
|
19
19
|
import { render, screen, waitFor } from "@testing-library/react";
|
|
20
20
|
import { ActivityTimeline } from "../activity-timeline";
|
|
21
|
-
import type { ActivityItem, UserBrief } from "
|
|
21
|
+
import type { ActivityItem, UserBrief } from "@/shared/story/types";
|
|
22
22
|
|
|
23
23
|
vi.mock("@/server/serverFns/project", () => ({
|
|
24
24
|
listProjectsBriefFn: vi.fn().mockResolvedValue([]),
|
|
@@ -46,7 +46,7 @@ vi.mock("@tanstack/react-router", () => ({
|
|
|
46
46
|
}));
|
|
47
47
|
|
|
48
48
|
import { AssociatedAttachmentsPanel } from "../story-detail-toolbar/associated-attachments-panel";
|
|
49
|
-
import type { RelationsData } from "
|
|
49
|
+
import type { RelationsData } from "@/shared/story/types";
|
|
50
50
|
|
|
51
51
|
const data = {
|
|
52
52
|
links: [{ id: "l1", url: "https://a.com", title: "站点A", description: null }],
|
|
@@ -193,7 +193,7 @@ const notionLikeReady = import("@/components/tiptap/notion-like");
|
|
|
193
193
|
|
|
194
194
|
// 被测组件(mock 声明之后 import,保证 mock 生效)
|
|
195
195
|
import { StoryDetailBody } from "../story-detail-body";
|
|
196
|
-
import type { StoryDetail, UserBrief } from "
|
|
196
|
+
import type { StoryDetail, UserBrief } from "@/shared/story/types";
|
|
197
197
|
// 真实契约类型(vi.mock 只替换运行时,类型仍取自真实模块 → 替身受契约约束)
|
|
198
198
|
import type {
|
|
199
199
|
CollabAwareness,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import { useState } from "react";
|
|
19
19
|
import { ActivityTimeline } from "./activity-timeline";
|
|
20
20
|
import { CommentThread } from "./comment-thread";
|
|
21
|
-
import type { CommentWithReplies, ActivityItem, UserBrief } from "
|
|
21
|
+
import type { CommentWithReplies, ActivityItem, UserBrief } from "@/shared/story/types";
|
|
22
22
|
import { m } from "@/paraglide/messages.js";
|
|
23
23
|
|
|
24
24
|
interface ActivityTabsProps {
|