@geminilight/mindos 0.6.29 → 0.6.31
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 +10 -4
- package/README_zh.md +10 -4
- package/app/app/api/acp/config/route.ts +82 -0
- package/app/app/api/acp/detect/route.ts +71 -48
- package/app/app/api/acp/install/route.ts +51 -0
- package/app/app/api/acp/session/route.ts +141 -11
- package/app/app/api/ask/route.ts +126 -18
- package/app/app/api/export/route.ts +105 -0
- package/app/app/api/workflows/route.ts +156 -0
- package/app/app/globals.css +2 -2
- package/app/app/page.tsx +7 -2
- package/app/app/trash/page.tsx +7 -0
- package/app/app/view/[...path]/ViewPageClient.tsx +234 -2
- package/app/components/ActivityBar.tsx +12 -4
- package/app/components/AskModal.tsx +4 -1
- package/app/components/ExportModal.tsx +220 -0
- package/app/components/FileTree.tsx +42 -11
- package/app/components/HomeContent.tsx +92 -20
- package/app/components/MarkdownView.tsx +45 -10
- package/app/components/Panel.tsx +1 -0
- package/app/components/RightAskPanel.tsx +5 -1
- package/app/components/Sidebar.tsx +10 -1
- package/app/components/SidebarLayout.tsx +6 -0
- package/app/components/TrashPageClient.tsx +263 -0
- package/app/components/agents/AgentDetailContent.tsx +263 -47
- package/app/components/agents/AgentsContentPage.tsx +11 -0
- package/app/components/agents/AgentsPanelA2aTab.tsx +285 -46
- package/app/components/agents/AgentsPanelSessionsTab.tsx +166 -0
- package/app/components/agents/agents-content-model.ts +2 -2
- package/app/components/ask/AgentSelectorCapsule.tsx +218 -0
- package/app/components/ask/AskContent.tsx +197 -239
- package/app/components/ask/FileChip.tsx +82 -17
- package/app/components/ask/MentionPopover.tsx +21 -3
- package/app/components/ask/MessageList.tsx +30 -9
- package/app/components/ask/SlashCommandPopover.tsx +21 -3
- package/app/components/ask/ToolCallBlock.tsx +102 -18
- package/app/components/changes/ChangesContentPage.tsx +58 -14
- package/app/components/explore/ExploreContent.tsx +4 -7
- package/app/components/explore/UseCaseCard.tsx +18 -1
- package/app/components/explore/use-cases.generated.ts +76 -0
- package/app/components/explore/use-cases.yaml +185 -0
- package/app/components/panels/AgentsPanel.tsx +1 -0
- package/app/components/panels/AgentsPanelHubNav.tsx +9 -2
- package/app/components/panels/DiscoverPanel.tsx +1 -1
- package/app/components/panels/WorkflowsPanel.tsx +206 -0
- package/app/components/renderers/workflow-yaml/StepEditor.tsx +164 -0
- package/app/components/renderers/workflow-yaml/WorkflowEditor.tsx +211 -0
- package/app/components/renderers/workflow-yaml/WorkflowRunner.tsx +269 -0
- package/app/components/renderers/workflow-yaml/WorkflowYamlRenderer.tsx +126 -0
- package/app/components/renderers/workflow-yaml/execution.ts +229 -0
- package/app/components/renderers/workflow-yaml/index.ts +6 -0
- package/app/components/renderers/workflow-yaml/manifest.ts +21 -0
- package/app/components/renderers/workflow-yaml/parser.ts +172 -0
- package/app/components/renderers/workflow-yaml/selectors.tsx +574 -0
- package/app/components/renderers/workflow-yaml/serializer.ts +56 -0
- package/app/components/renderers/workflow-yaml/types.ts +46 -0
- package/app/components/settings/AiTab.tsx +191 -174
- package/app/components/settings/AppearanceTab.tsx +168 -77
- package/app/components/settings/KnowledgeTab.tsx +131 -136
- package/app/components/settings/McpTab.tsx +11 -11
- package/app/components/settings/Primitives.tsx +60 -0
- package/app/components/settings/SettingsContent.tsx +15 -8
- package/app/components/settings/SyncTab.tsx +12 -12
- package/app/components/settings/UninstallTab.tsx +8 -18
- package/app/components/settings/UpdateTab.tsx +82 -82
- package/app/components/settings/types.ts +17 -8
- package/app/hooks/useAcpConfig.ts +96 -0
- package/app/hooks/useAcpDetection.ts +69 -14
- package/app/hooks/useAcpRegistry.ts +46 -11
- package/app/hooks/useAskModal.ts +12 -5
- package/app/hooks/useAskPanel.ts +8 -5
- package/app/hooks/useAskSession.ts +19 -2
- package/app/hooks/useImageUpload.ts +152 -0
- package/app/lib/acp/acp-tools.ts +3 -1
- package/app/lib/acp/agent-descriptors.ts +274 -0
- package/app/lib/acp/bridge.ts +6 -0
- package/app/lib/acp/index.ts +20 -4
- package/app/lib/acp/registry.ts +74 -7
- package/app/lib/acp/session.ts +490 -28
- package/app/lib/acp/subprocess.ts +307 -21
- package/app/lib/acp/types.ts +158 -20
- package/app/lib/actions.ts +57 -3
- package/app/lib/agent/model.ts +18 -3
- package/app/lib/agent/stream-consumer.ts +18 -0
- package/app/lib/agent/to-agent-messages.ts +25 -2
- package/app/lib/agent/tools.ts +56 -9
- package/app/lib/core/export.ts +116 -0
- package/app/lib/core/trash.ts +241 -0
- package/app/lib/fs.ts +47 -0
- package/app/lib/hooks/usePinnedFiles.ts +90 -0
- package/app/lib/i18n/generated/explore-i18n.generated.ts +138 -0
- package/app/lib/i18n/index.ts +3 -0
- package/app/lib/i18n/modules/knowledge.ts +124 -6
- package/app/lib/i18n/modules/navigation.ts +2 -0
- package/app/lib/i18n/modules/onboarding.ts +2 -134
- package/app/lib/i18n/modules/panels.ts +146 -2
- package/app/lib/i18n/modules/settings.ts +12 -0
- package/app/lib/pi-integration/skills.ts +21 -6
- package/app/lib/renderers/index.ts +2 -2
- package/app/lib/settings.ts +10 -0
- package/app/lib/types.ts +12 -1
- package/app/next-env.d.ts +1 -1
- package/app/package.json +11 -3
- package/app/scripts/generate-explore.ts +145 -0
- package/package.json +1 -1
- package/templates/en/.mindos/workflows/Sprint Release.flow.yaml +130 -0
- package/templates/zh/.mindos/workflows//345/221/250/350/277/255/344/273/243/346/243/200/346/237/245.flow.yaml +84 -0
- package/app/components/explore/use-cases.ts +0 -58
- package/app/components/renderers/workflow/WorkflowRenderer.tsx +0 -409
- package/app/components/renderers/workflow/manifest.ts +0 -14
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// ⚠️ AUTO-GENERATED — DO NOT EDIT. Source: components/explore/use-cases.yaml
|
|
2
|
+
// Run `npm run generate` to regenerate.
|
|
3
|
+
|
|
4
|
+
/** Capability axis — maps to product pillars */
|
|
5
|
+
export type UseCaseCategory = 'knowledge-management' | 'memory-sync' | 'auto-execute' | 'experience-evolution' | 'human-insights' | 'audit-control';
|
|
6
|
+
|
|
7
|
+
/** Scenario axis — maps to user journey phase */
|
|
8
|
+
export type UseCaseScenario = 'first-day' | 'daily' | 'project' | 'advanced';
|
|
9
|
+
|
|
10
|
+
export interface UseCase {
|
|
11
|
+
id: string;
|
|
12
|
+
icon: string;
|
|
13
|
+
image?: string;
|
|
14
|
+
category: UseCaseCategory;
|
|
15
|
+
scenario: UseCaseScenario;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const useCases: UseCase[] = [
|
|
19
|
+
{
|
|
20
|
+
"id": "c1",
|
|
21
|
+
"icon": "👤",
|
|
22
|
+
"category": "memory-sync",
|
|
23
|
+
"scenario": "first-day"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"id": "c2",
|
|
27
|
+
"icon": "📥",
|
|
28
|
+
"category": "knowledge-management",
|
|
29
|
+
"scenario": "daily"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"id": "c3",
|
|
33
|
+
"icon": "🔄",
|
|
34
|
+
"category": "memory-sync",
|
|
35
|
+
"scenario": "project"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"id": "c4",
|
|
39
|
+
"icon": "🔁",
|
|
40
|
+
"category": "experience-evolution",
|
|
41
|
+
"scenario": "daily"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "c5",
|
|
45
|
+
"icon": "💡",
|
|
46
|
+
"category": "auto-execute",
|
|
47
|
+
"scenario": "daily"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "c6",
|
|
51
|
+
"icon": "🚀",
|
|
52
|
+
"category": "auto-execute",
|
|
53
|
+
"scenario": "project"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"id": "c7",
|
|
57
|
+
"icon": "🔍",
|
|
58
|
+
"category": "knowledge-management",
|
|
59
|
+
"scenario": "project"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "c8",
|
|
63
|
+
"icon": "🤝",
|
|
64
|
+
"category": "human-insights",
|
|
65
|
+
"scenario": "daily"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "c9",
|
|
69
|
+
"icon": "🛡️",
|
|
70
|
+
"category": "audit-control",
|
|
71
|
+
"scenario": "advanced"
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
export const categories: UseCaseCategory[] = ["knowledge-management","memory-sync","auto-execute","experience-evolution","human-insights","audit-control"];
|
|
76
|
+
export const scenarios: UseCaseScenario[] = ["first-day","daily","project","advanced"];
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# MindOS Explore — Use Case Definitions (Single Source of Truth)
|
|
2
|
+
#
|
|
3
|
+
# To add a new use case: just append a new entry below.
|
|
4
|
+
# Run `npm run generate` to regenerate use-cases.ts + i18n.
|
|
5
|
+
#
|
|
6
|
+
# Fields:
|
|
7
|
+
# id: Unique identifier (e.g. c10)
|
|
8
|
+
# icon: Emoji fallback when no image is available
|
|
9
|
+
# image: (optional) Custom image path under /explore/. Omit to use /explore/{id}.png
|
|
10
|
+
# category: One of: knowledge-management | memory-sync | auto-execute | experience-evolution | human-insights | audit-control
|
|
11
|
+
# scenario: One of: first-day | daily | project | advanced
|
|
12
|
+
# zh/en: Bilingual text — title, desc, prompt
|
|
13
|
+
|
|
14
|
+
# ── Category & Scenario labels (used by filter UI) ──
|
|
15
|
+
meta:
|
|
16
|
+
categories:
|
|
17
|
+
knowledge-management:
|
|
18
|
+
en: Knowledge Management
|
|
19
|
+
zh: 知识管理
|
|
20
|
+
memory-sync:
|
|
21
|
+
en: Memory Sync
|
|
22
|
+
zh: 记忆同步
|
|
23
|
+
auto-execute:
|
|
24
|
+
en: Auto Execute
|
|
25
|
+
zh: 自动执行
|
|
26
|
+
experience-evolution:
|
|
27
|
+
en: Experience Evolution
|
|
28
|
+
zh: 经验进化
|
|
29
|
+
human-insights:
|
|
30
|
+
en: Human Insights
|
|
31
|
+
zh: 人类洞察
|
|
32
|
+
audit-control:
|
|
33
|
+
en: Audit & Control
|
|
34
|
+
zh: 审计纠错
|
|
35
|
+
scenarios:
|
|
36
|
+
first-day:
|
|
37
|
+
en: First Day
|
|
38
|
+
zh: 初次使用
|
|
39
|
+
daily:
|
|
40
|
+
en: Daily Work
|
|
41
|
+
zh: 日常工作
|
|
42
|
+
project:
|
|
43
|
+
en: Project Work
|
|
44
|
+
zh: 项目协作
|
|
45
|
+
advanced:
|
|
46
|
+
en: Advanced
|
|
47
|
+
zh: 高级
|
|
48
|
+
ui:
|
|
49
|
+
title:
|
|
50
|
+
en: Explore Use Cases
|
|
51
|
+
zh: 探索使用场景
|
|
52
|
+
subtitle:
|
|
53
|
+
en: "Discover what you can do with MindOS \u2014 pick a scenario and try it now."
|
|
54
|
+
zh: "发现 MindOS 能帮你做什么 \u2014 选一个场景,立即体验。"
|
|
55
|
+
tryIt:
|
|
56
|
+
en: Try it
|
|
57
|
+
zh: 试一试
|
|
58
|
+
all:
|
|
59
|
+
en: All
|
|
60
|
+
zh: 全部
|
|
61
|
+
byCapability:
|
|
62
|
+
en: By Capability
|
|
63
|
+
zh: 按能力
|
|
64
|
+
byScenario:
|
|
65
|
+
en: By Scenario
|
|
66
|
+
zh: 按场景
|
|
67
|
+
|
|
68
|
+
# ── Use Cases ──
|
|
69
|
+
cases:
|
|
70
|
+
- id: c1
|
|
71
|
+
icon: "\U0001F464"
|
|
72
|
+
category: memory-sync
|
|
73
|
+
scenario: first-day
|
|
74
|
+
en:
|
|
75
|
+
title: Inject Your Identity
|
|
76
|
+
desc: "Tell all AI agents who you are \u2014 preferences, tech stack, communication style \u2014 in one shot."
|
|
77
|
+
prompt: "Here's my resume, read it and organize my info into MindOS."
|
|
78
|
+
zh:
|
|
79
|
+
title: 注入身份
|
|
80
|
+
desc: "让所有 AI Agent 一次认识你 \u2014 偏好、技术栈、沟通风格。"
|
|
81
|
+
prompt: 这是我的简历,读一下,把我的信息整理到 MindOS 里。
|
|
82
|
+
|
|
83
|
+
- id: c2
|
|
84
|
+
icon: "\U0001F4E5"
|
|
85
|
+
category: knowledge-management
|
|
86
|
+
scenario: daily
|
|
87
|
+
en:
|
|
88
|
+
title: Save Information
|
|
89
|
+
desc: Archive articles, meeting notes, or web pages into your knowledge base with one prompt.
|
|
90
|
+
prompt: Help me save the key points from this article into MindOS.
|
|
91
|
+
zh:
|
|
92
|
+
title: 注入信息
|
|
93
|
+
desc: 一句话归档文章、会议纪要或网页到知识库,全局可搜。
|
|
94
|
+
prompt: 帮我把这篇文章的要点整理到 MindOS 里。
|
|
95
|
+
|
|
96
|
+
- id: c3
|
|
97
|
+
icon: "\U0001F504"
|
|
98
|
+
category: memory-sync
|
|
99
|
+
scenario: project
|
|
100
|
+
en:
|
|
101
|
+
title: Cross-Agent Handoff
|
|
102
|
+
desc: "Start a plan in MindOS, continue coding in Claude Code, refine in Cursor \u2014 zero context loss."
|
|
103
|
+
prompt: Help me start coding based on the plan in MindOS.
|
|
104
|
+
zh:
|
|
105
|
+
title: 跨 Agent 切换
|
|
106
|
+
desc: "在 MindOS 写方案,在 Claude Code 写代码,在 Cursor 优化 \u2014 零重复。"
|
|
107
|
+
prompt: 帮我按 MindOS 里的 XXX 方案开始写代码。
|
|
108
|
+
|
|
109
|
+
- id: c4
|
|
110
|
+
icon: "\U0001F501"
|
|
111
|
+
category: experience-evolution
|
|
112
|
+
scenario: daily
|
|
113
|
+
en:
|
|
114
|
+
title: "Experience \u2192 SOP"
|
|
115
|
+
desc: Turn hard-won debugging sessions into reusable workflows that prevent future mistakes.
|
|
116
|
+
prompt: Help me distill this conversation into a reusable workflow in MindOS.
|
|
117
|
+
zh:
|
|
118
|
+
title: "经验\u2192SOP"
|
|
119
|
+
desc: 把踩坑经验沉淀为可复用的工作流,下次 3 分钟搞定。
|
|
120
|
+
prompt: 帮我把这次对话的经验沉淀到 MindOS,形成可复用的工作流。
|
|
121
|
+
|
|
122
|
+
- id: c5
|
|
123
|
+
icon: "\U0001F4A1"
|
|
124
|
+
category: auto-execute
|
|
125
|
+
scenario: daily
|
|
126
|
+
en:
|
|
127
|
+
title: Capture Ideas on the Go
|
|
128
|
+
desc: "Jot down an inspiration on your phone \u2014 MindOS archives, decomposes, and assigns to agents."
|
|
129
|
+
prompt: Help me organize this idea into MindOS and break it into actionable sub-tasks.
|
|
130
|
+
zh:
|
|
131
|
+
title: 手机记灵感
|
|
132
|
+
desc: 随手记下灵感,MindOS 自动归档、拆任务、多 Agent 接力执行。
|
|
133
|
+
prompt: 帮我把这个想法整理到 MindOS,拆解成可执行的子任务。
|
|
134
|
+
|
|
135
|
+
- id: c6
|
|
136
|
+
icon: "\U0001F680"
|
|
137
|
+
category: auto-execute
|
|
138
|
+
scenario: project
|
|
139
|
+
en:
|
|
140
|
+
title: Project Cold Start
|
|
141
|
+
desc: "Spin up a new project in 4 minutes \u2014 your profile and SOPs guide the scaffolding automatically."
|
|
142
|
+
prompt: Help me start a new project following the Startup SOP in MindOS.
|
|
143
|
+
zh:
|
|
144
|
+
title: 项目冷启动
|
|
145
|
+
desc: "4 分钟搭建新项目 \u2014 Profile 和 SOP 自动引导脚手架。"
|
|
146
|
+
prompt: 帮我按 MindOS 里的 Startup SOP 启动一个新项目。
|
|
147
|
+
|
|
148
|
+
- id: c7
|
|
149
|
+
icon: "\U0001F50D"
|
|
150
|
+
category: knowledge-management
|
|
151
|
+
scenario: project
|
|
152
|
+
en:
|
|
153
|
+
title: Research & Archive
|
|
154
|
+
desc: Let agents research competitors or topics for you, then file structured results in your KB.
|
|
155
|
+
prompt: "Help me research X, Y, Z products and save results to the MindOS product library."
|
|
156
|
+
zh:
|
|
157
|
+
title: 调研入库
|
|
158
|
+
desc: 让 Agent 替你跑腿调研竞品或话题,结果结构化入库。
|
|
159
|
+
prompt: 帮我调研 X、Y、Z 这几个产品,结果写入 MindOS 产品库。
|
|
160
|
+
|
|
161
|
+
- id: c8
|
|
162
|
+
icon: "\U0001F91D"
|
|
163
|
+
category: human-insights
|
|
164
|
+
scenario: daily
|
|
165
|
+
en:
|
|
166
|
+
title: Network Management
|
|
167
|
+
desc: "Log conversations with contacts, auto-generate follow-up TODOs, and keep full context."
|
|
168
|
+
prompt: "I met with someone today \u2014 update MindOS Connections and create follow-up TODOs."
|
|
169
|
+
zh:
|
|
170
|
+
title: 人脉管理
|
|
171
|
+
desc: 记录对话、自动生成跟进待办,每个联系人都有完整上下文。
|
|
172
|
+
prompt: 我今天和 XXX 聊了这些内容,帮我更新到 MindOS 并生成跟进待办。
|
|
173
|
+
|
|
174
|
+
- id: c9
|
|
175
|
+
icon: "\U0001F6E1\uFE0F"
|
|
176
|
+
category: audit-control
|
|
177
|
+
scenario: advanced
|
|
178
|
+
en:
|
|
179
|
+
title: Audit & Correct
|
|
180
|
+
desc: "Review what agents know about you, fix mistakes in one place, and all agents update instantly."
|
|
181
|
+
prompt: Check my MindOS Profile for accuracy and correct any errors.
|
|
182
|
+
zh:
|
|
183
|
+
title: 审计纠偏
|
|
184
|
+
desc: 审查 Agent 记了什么,一处修正,全局生效。
|
|
185
|
+
prompt: 帮我检查 MindOS Profile 里的技术栈偏好是否正确,有错误帮我修正。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { usePathname, useSearchParams } from 'next/navigation';
|
|
4
|
-
import { Globe, LayoutDashboard, Server, Zap } from 'lucide-react';
|
|
4
|
+
import { Globe, History, LayoutDashboard, Server, Zap } from 'lucide-react';
|
|
5
5
|
import { PanelNavRow } from './PanelNavRow';
|
|
6
6
|
|
|
7
7
|
type HubCopy = {
|
|
@@ -9,6 +9,7 @@ type HubCopy = {
|
|
|
9
9
|
navMcp: string;
|
|
10
10
|
navSkills: string;
|
|
11
11
|
navNetwork: string;
|
|
12
|
+
navSessions: string;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
export function AgentsPanelHubNav({
|
|
@@ -26,7 +27,7 @@ export function AgentsPanelHubNav({
|
|
|
26
27
|
return (
|
|
27
28
|
<div className="py-2">
|
|
28
29
|
<PanelNavRow
|
|
29
|
-
icon={<LayoutDashboard size={14} className=
|
|
30
|
+
icon={<LayoutDashboard size={14} className={inAgentsRoute && (tab === null || tab === 'overview') ? 'text-[var(--amber)]' : 'text-muted-foreground'} />}
|
|
30
31
|
title={copy.navOverview}
|
|
31
32
|
badge={<span className="text-2xs tabular-nums text-muted-foreground/60 px-1.5 py-0.5 rounded bg-muted/40 font-medium">{connectedCount}</span>}
|
|
32
33
|
href="/agents"
|
|
@@ -50,6 +51,12 @@ export function AgentsPanelHubNav({
|
|
|
50
51
|
href="/agents?tab=a2a"
|
|
51
52
|
active={inAgentsRoute && tab === 'a2a'}
|
|
52
53
|
/>
|
|
54
|
+
<PanelNavRow
|
|
55
|
+
icon={<History size={14} className={inAgentsRoute && tab === 'sessions' ? 'text-[var(--amber)]' : 'text-muted-foreground'} />}
|
|
56
|
+
title={copy.navSessions}
|
|
57
|
+
href="/agents?tab=sessions"
|
|
58
|
+
active={inAgentsRoute && tab === 'sessions'}
|
|
59
|
+
/>
|
|
53
60
|
</div>
|
|
54
61
|
);
|
|
55
62
|
}
|
|
@@ -6,7 +6,7 @@ import { Lightbulb, Blocks, Zap, LayoutTemplate, User, Download, RefreshCw, Repe
|
|
|
6
6
|
import PanelHeader from './PanelHeader';
|
|
7
7
|
import { PanelNavRow, ComingSoonBadge } from './PanelNavRow';
|
|
8
8
|
import { useLocale } from '@/lib/LocaleContext';
|
|
9
|
-
import { useCases } from '@/components/explore/use-cases';
|
|
9
|
+
import { useCases } from '@/components/explore/use-cases.generated';
|
|
10
10
|
import { openAskModal } from '@/hooks/useAskModal';
|
|
11
11
|
import { getPluginRenderers, isRendererEnabled, setRendererEnabled, loadDisabledState } from '@/lib/renderers/registry';
|
|
12
12
|
import { Toggle } from '../settings/Primitives';
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
5
|
+
import { Plus, Zap, AlertTriangle, Loader2 } from 'lucide-react';
|
|
6
|
+
import PanelHeader from './PanelHeader';
|
|
7
|
+
import { useLocale } from '@/lib/LocaleContext';
|
|
8
|
+
import { encodePath, relativeTime } from '@/lib/utils';
|
|
9
|
+
|
|
10
|
+
interface WorkflowItem {
|
|
11
|
+
path: string;
|
|
12
|
+
fileName: string;
|
|
13
|
+
title: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
stepCount: number;
|
|
16
|
+
mtime: number;
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface WorkflowsPanelProps {
|
|
21
|
+
active: boolean;
|
|
22
|
+
maximized?: boolean;
|
|
23
|
+
onMaximize?: () => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export default function WorkflowsPanel({ active, maximized, onMaximize }: WorkflowsPanelProps) {
|
|
27
|
+
const { t } = useLocale();
|
|
28
|
+
const wt = t.panels.workflows as {
|
|
29
|
+
title: string;
|
|
30
|
+
empty: string;
|
|
31
|
+
emptyDesc: string;
|
|
32
|
+
newWorkflow: string;
|
|
33
|
+
nSteps: (n: number) => string;
|
|
34
|
+
parseError: string;
|
|
35
|
+
name: string;
|
|
36
|
+
namePlaceholder: string;
|
|
37
|
+
template: string;
|
|
38
|
+
templateBlank: string;
|
|
39
|
+
create: string;
|
|
40
|
+
cancel: string;
|
|
41
|
+
creating: string;
|
|
42
|
+
exists: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const [workflows, setWorkflows] = useState<WorkflowItem[]>([]);
|
|
46
|
+
const [loading, setLoading] = useState(true);
|
|
47
|
+
const [showCreate, setShowCreate] = useState(false);
|
|
48
|
+
const [newName, setNewName] = useState('');
|
|
49
|
+
const [creating, setCreating] = useState(false);
|
|
50
|
+
const [createError, setCreateError] = useState('');
|
|
51
|
+
|
|
52
|
+
const fetchWorkflows = useCallback(async () => {
|
|
53
|
+
try {
|
|
54
|
+
const res = await fetch('/api/workflows');
|
|
55
|
+
if (!res.ok) return;
|
|
56
|
+
const data = await res.json();
|
|
57
|
+
setWorkflows(data.workflows ?? []);
|
|
58
|
+
} catch {
|
|
59
|
+
// silent
|
|
60
|
+
} finally {
|
|
61
|
+
setLoading(false);
|
|
62
|
+
}
|
|
63
|
+
}, []);
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (active) fetchWorkflows();
|
|
67
|
+
}, [active, fetchWorkflows]);
|
|
68
|
+
|
|
69
|
+
const handleCreate = async () => {
|
|
70
|
+
const name = newName.trim();
|
|
71
|
+
if (!name || creating) return;
|
|
72
|
+
setCreating(true);
|
|
73
|
+
setCreateError('');
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const res = await fetch('/api/workflows', {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: { 'Content-Type': 'application/json' },
|
|
79
|
+
body: JSON.stringify({ name }),
|
|
80
|
+
});
|
|
81
|
+
const data = await res.json();
|
|
82
|
+
if (!res.ok) {
|
|
83
|
+
setCreateError(res.status === 409 ? wt.exists : (data.error || 'Error'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// Refresh list and navigate to new file
|
|
87
|
+
setShowCreate(false);
|
|
88
|
+
setNewName('');
|
|
89
|
+
await fetchWorkflows();
|
|
90
|
+
window.location.href = `/view/${encodePath(data.path)}`;
|
|
91
|
+
} catch {
|
|
92
|
+
setCreateError('Network error');
|
|
93
|
+
} finally {
|
|
94
|
+
setCreating(false);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<div className={`flex flex-col h-full ${active ? '' : 'hidden'}`}>
|
|
100
|
+
<PanelHeader title={wt.title} maximized={maximized} onMaximize={onMaximize}>
|
|
101
|
+
<button
|
|
102
|
+
onClick={() => setShowCreate(v => !v)}
|
|
103
|
+
className="p-1 rounded hover:bg-muted text-muted-foreground hover:text-foreground transition-colors focus-visible:ring-1 focus-visible:ring-ring"
|
|
104
|
+
aria-label={wt.newWorkflow}
|
|
105
|
+
title={wt.newWorkflow}
|
|
106
|
+
>
|
|
107
|
+
<Plus size={13} />
|
|
108
|
+
</button>
|
|
109
|
+
</PanelHeader>
|
|
110
|
+
|
|
111
|
+
<div className="flex-1 overflow-y-auto min-h-0">
|
|
112
|
+
{/* Create form */}
|
|
113
|
+
{showCreate && (
|
|
114
|
+
<div className="px-3 py-3 border-b border-border">
|
|
115
|
+
<label className="block text-xs font-medium text-muted-foreground mb-1.5">{wt.name}</label>
|
|
116
|
+
<input
|
|
117
|
+
type="text"
|
|
118
|
+
value={newName}
|
|
119
|
+
onChange={e => { setNewName(e.target.value); setCreateError(''); }}
|
|
120
|
+
onKeyDown={e => e.key === 'Enter' && handleCreate()}
|
|
121
|
+
placeholder={wt.namePlaceholder}
|
|
122
|
+
autoFocus
|
|
123
|
+
className="w-full px-2.5 py-1.5 text-sm rounded-md border border-border bg-background text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
|
|
124
|
+
/>
|
|
125
|
+
{createError && (
|
|
126
|
+
<p className="text-xs text-[var(--error)] mt-1">{createError}</p>
|
|
127
|
+
)}
|
|
128
|
+
<div className="flex gap-2 mt-2.5">
|
|
129
|
+
<button
|
|
130
|
+
onClick={() => { setShowCreate(false); setNewName(''); setCreateError(''); }}
|
|
131
|
+
className="flex-1 px-3 py-1.5 text-xs rounded-md border border-border text-muted-foreground hover:bg-muted transition-colors"
|
|
132
|
+
>
|
|
133
|
+
{wt.cancel}
|
|
134
|
+
</button>
|
|
135
|
+
<button
|
|
136
|
+
onClick={handleCreate}
|
|
137
|
+
disabled={!newName.trim() || creating}
|
|
138
|
+
className="flex-1 px-3 py-1.5 text-xs rounded-md font-medium transition-colors disabled:opacity-50 bg-[var(--amber)] text-[var(--amber-foreground)]"
|
|
139
|
+
>
|
|
140
|
+
{creating ? wt.creating : wt.create}
|
|
141
|
+
</button>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
{/* Loading */}
|
|
147
|
+
{loading && (
|
|
148
|
+
<div className="flex items-center justify-center py-8 text-muted-foreground">
|
|
149
|
+
<Loader2 size={16} className="animate-spin" />
|
|
150
|
+
</div>
|
|
151
|
+
)}
|
|
152
|
+
|
|
153
|
+
{/* Empty state */}
|
|
154
|
+
{!loading && workflows.length === 0 && !showCreate && (
|
|
155
|
+
<div className="flex flex-col items-center justify-center py-10 px-4 text-center">
|
|
156
|
+
<Zap size={24} className="text-muted-foreground/40 mb-3" />
|
|
157
|
+
<p className="text-sm font-medium text-muted-foreground mb-1">{wt.empty}</p>
|
|
158
|
+
<p className="text-xs text-muted-foreground/70 mb-4 max-w-[200px]">{wt.emptyDesc}</p>
|
|
159
|
+
<button
|
|
160
|
+
onClick={() => setShowCreate(true)}
|
|
161
|
+
className="inline-flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-md font-medium bg-[var(--amber)] text-[var(--amber-foreground)] transition-colors"
|
|
162
|
+
>
|
|
163
|
+
<Plus size={12} />
|
|
164
|
+
{wt.newWorkflow}
|
|
165
|
+
</button>
|
|
166
|
+
</div>
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
{/* Workflow list */}
|
|
170
|
+
{!loading && workflows.length > 0 && (
|
|
171
|
+
<div className="flex flex-col gap-0.5 py-1.5">
|
|
172
|
+
{workflows.map(w => (
|
|
173
|
+
<Link
|
|
174
|
+
key={w.path}
|
|
175
|
+
href={`/view/${encodePath(w.path)}`}
|
|
176
|
+
className={`flex items-start gap-2.5 px-3 py-2 mx-1 rounded-lg transition-colors hover:bg-muted ${
|
|
177
|
+
w.error ? 'opacity-70' : ''
|
|
178
|
+
}`}
|
|
179
|
+
>
|
|
180
|
+
<Zap size={14} className="shrink-0 mt-0.5 text-[var(--amber)]" />
|
|
181
|
+
<div className="min-w-0 flex-1">
|
|
182
|
+
<span className="text-sm font-medium text-foreground block truncate">{w.title}</span>
|
|
183
|
+
<div className="flex items-center gap-2 mt-0.5">
|
|
184
|
+
{w.error ? (
|
|
185
|
+
<span className="inline-flex items-center gap-1 text-2xs text-[var(--error)]">
|
|
186
|
+
<AlertTriangle size={10} />
|
|
187
|
+
{wt.parseError}
|
|
188
|
+
</span>
|
|
189
|
+
) : (
|
|
190
|
+
<span className="text-2xs text-muted-foreground">
|
|
191
|
+
{wt.nSteps(w.stepCount)}
|
|
192
|
+
</span>
|
|
193
|
+
)}
|
|
194
|
+
<span className="text-2xs text-muted-foreground/60" suppressHydrationWarning>
|
|
195
|
+
{relativeTime(w.mtime, t.home?.relativeTime)}
|
|
196
|
+
</span>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
</Link>
|
|
200
|
+
))}
|
|
201
|
+
</div>
|
|
202
|
+
)}
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
}
|