@leanspec/ui 0.2.13 → 0.2.15-dev.21022397862

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 (149) hide show
  1. package/bin/leanspec-ui.js +191 -0
  2. package/dist/assets/_baseUniq-CRqreL7N.js +1 -0
  3. package/dist/assets/arc-DMhx9AJT.js +1 -0
  4. package/dist/assets/architectureDiagram-VXUJARFQ-DM0L0YzO.js +36 -0
  5. package/dist/assets/blockDiagram-VD42YOAC-DHQXDHsD.js +122 -0
  6. package/dist/assets/c4Diagram-YG6GDRKO-0L7o2gpH.js +10 -0
  7. package/dist/assets/channel-2tOl0nAZ.js +1 -0
  8. package/dist/assets/chunk-4BX2VUAB-CwFT-Uaj.js +1 -0
  9. package/dist/assets/chunk-55IACEB6-CjvuUHHG.js +1 -0
  10. package/dist/assets/chunk-B4BG7PRW-BRJBysMK.js +165 -0
  11. package/dist/assets/chunk-DI55MBZ5-BnNEeoaA.js +220 -0
  12. package/dist/assets/chunk-FMBD7UC4-BK2l30pm.js +15 -0
  13. package/dist/assets/chunk-QN33PNHL-BN_cZkCU.js +1 -0
  14. package/dist/assets/chunk-QZHKN3VN-Brc3Yrub.js +1 -0
  15. package/dist/assets/chunk-TZMSLE5B-D2zzpLfO.js +1 -0
  16. package/dist/assets/classDiagram-2ON5EDUG-BB9CSNmS.js +1 -0
  17. package/dist/assets/classDiagram-v2-WZHVMYZB-BB9CSNmS.js +1 -0
  18. package/dist/assets/clone-BjxVFtyI.js +1 -0
  19. package/dist/assets/core-DV6XEvTN.js +1 -0
  20. package/dist/assets/cose-bilkent-S5V4N54A-CLJgM3XR.js +1 -0
  21. package/dist/assets/cytoscape.esm-5J0xJHOV.js +321 -0
  22. package/dist/assets/dagre-6UL2VRFP-_IFvBJKJ.js +4 -0
  23. package/dist/assets/diagram-PSM6KHXK--83HIYSQ.js +24 -0
  24. package/dist/assets/diagram-QEK2KX5R-6jAWnCnZ.js +43 -0
  25. package/dist/assets/diagram-S2PKOQOG-D5pwHvjZ.js +24 -0
  26. package/dist/assets/erDiagram-Q2GNP2WA-B4FV3mTd.js +60 -0
  27. package/dist/assets/flowDiagram-NV44I4VS-mtD2kF4M.js +162 -0
  28. package/dist/assets/ganttDiagram-JELNMOA3-BKALgqTK.js +267 -0
  29. package/dist/assets/gitGraphDiagram-NY62KEGX-Bd7r0pAf.js +65 -0
  30. package/dist/assets/graph-B2rEI7cK.js +1 -0
  31. package/dist/assets/index-Bekv_o1t.css +1 -0
  32. package/dist/assets/index-DSRxU-E5.js +389 -0
  33. package/dist/assets/infoDiagram-WHAUD3N6--nJOBKqh.js +2 -0
  34. package/dist/assets/journeyDiagram-XKPGCS4Q-BzGutKN3.js +139 -0
  35. package/dist/assets/kanban-definition-3W4ZIXB7-DyQO17vq.js +89 -0
  36. package/dist/assets/katex-XbL3y5x-.js +261 -0
  37. package/dist/assets/layout-iCSHU015.js +1 -0
  38. package/dist/assets/min-BK_AIJdo.js +1 -0
  39. package/dist/assets/mindmap-definition-VGOIOE7T-BZMj_6zo.js +68 -0
  40. package/dist/assets/pieDiagram-ADFJNKIX-CkAGsq9p.js +30 -0
  41. package/dist/assets/quadrantDiagram-AYHSOK5B-CWa93px1.js +7 -0
  42. package/dist/assets/requirementDiagram-UZGBJVZJ-CufFVR8c.js +64 -0
  43. package/dist/assets/sankeyDiagram-TZEHDZUN-BEPgVgU4.js +10 -0
  44. package/dist/assets/sequenceDiagram-WL72ISMW-BkdBWhel.js +145 -0
  45. package/dist/assets/stateDiagram-FKZM4ZOC-D5T73yx0.js +1 -0
  46. package/dist/assets/stateDiagram-v2-4FDKWEC3-9hJWG2n6.js +1 -0
  47. package/dist/assets/timeline-definition-IT6M3QCI-CX7kTdU2.js +61 -0
  48. package/dist/assets/treemap-KMMF4GRG-ftWCQ9lJ.js +128 -0
  49. package/dist/assets/xychartDiagram-PRI3JC2R-Ngrels4n.js +7 -0
  50. package/{index.html → dist/index.html} +2 -1
  51. package/package.json +13 -3
  52. package/eslint.config.js +0 -23
  53. package/postcss.config.js +0 -6
  54. package/src/App.css +0 -42
  55. package/src/App.tsx +0 -17
  56. package/src/assets/react.svg +0 -1
  57. package/src/components/LanguageSwitcher.tsx +0 -67
  58. package/src/components/Layout.tsx +0 -88
  59. package/src/components/MainSidebar.tsx +0 -163
  60. package/src/components/MermaidDiagram.tsx +0 -85
  61. package/src/components/MinimalLayout.tsx +0 -51
  62. package/src/components/Navigation.tsx +0 -254
  63. package/src/components/PriorityBadge.tsx +0 -59
  64. package/src/components/ProjectSwitcher.tsx +0 -222
  65. package/src/components/QuickSearch.tsx +0 -225
  66. package/src/components/RootRedirect.tsx +0 -40
  67. package/src/components/SpecDetailLayout.context.ts +0 -10
  68. package/src/components/SpecDetailLayout.tsx +0 -14
  69. package/src/components/SpecsNavSidebar.tsx +0 -615
  70. package/src/components/StatusBadge.tsx +0 -59
  71. package/src/components/ThemeToggle.tsx +0 -25
  72. package/src/components/Tooltip.tsx +0 -29
  73. package/src/components/context/ContextClient.tsx +0 -471
  74. package/src/components/context/ContextFileDetail.tsx +0 -163
  75. package/src/components/dashboard/ActivityItem.tsx +0 -36
  76. package/src/components/dashboard/DashboardClient.tsx +0 -218
  77. package/src/components/dashboard/SpecListItem.tsx +0 -58
  78. package/src/components/dashboard/StatCard.tsx +0 -52
  79. package/src/components/dependencies/SpecNode.tsx +0 -128
  80. package/src/components/dependencies/SpecSidebar.tsx +0 -256
  81. package/src/components/dependencies/constants.ts +0 -25
  82. package/src/components/dependencies/types.ts +0 -38
  83. package/src/components/dependencies/utils.ts +0 -261
  84. package/src/components/metadata-editors/PriorityEditor.tsx +0 -89
  85. package/src/components/metadata-editors/StatusEditor.tsx +0 -85
  86. package/src/components/metadata-editors/TagsEditor.tsx +0 -207
  87. package/src/components/projects/CreateProjectDialog.tsx +0 -162
  88. package/src/components/projects/DirectoryPicker.tsx +0 -182
  89. package/src/components/shared/BackToTop.tsx +0 -39
  90. package/src/components/shared/ColorPicker.tsx +0 -68
  91. package/src/components/shared/EmptyState.tsx +0 -35
  92. package/src/components/shared/ErrorBoundary.tsx +0 -79
  93. package/src/components/shared/PageHeader.tsx +0 -23
  94. package/src/components/shared/PageTransition.tsx +0 -40
  95. package/src/components/shared/ProjectAvatar.tsx +0 -107
  96. package/src/components/shared/Skeletons.tsx +0 -184
  97. package/src/components/spec-detail/EditableMetadata.tsx +0 -129
  98. package/src/components/spec-detail/MarkdownRenderer.tsx +0 -47
  99. package/src/components/spec-detail/TableOfContents.tsx +0 -150
  100. package/src/components/specs/BoardView.tsx +0 -204
  101. package/src/components/specs/ListView.tsx +0 -62
  102. package/src/components/specs/SpecsFilters.tsx +0 -190
  103. package/src/contexts/KeyboardShortcutsContext.tsx +0 -95
  104. package/src/contexts/LayoutContext.tsx +0 -45
  105. package/src/contexts/ProjectContext.tsx +0 -163
  106. package/src/contexts/ThemeContext.tsx +0 -90
  107. package/src/contexts/index.ts +0 -7
  108. package/src/hooks/useKeyboardShortcuts.ts +0 -87
  109. package/src/index.css +0 -624
  110. package/src/lib/api.ts +0 -72
  111. package/src/lib/backend-adapter.ts +0 -382
  112. package/src/lib/date-utils.ts +0 -122
  113. package/src/lib/i18n.test.ts +0 -57
  114. package/src/lib/i18n.ts +0 -51
  115. package/src/lib/markdown-utils.ts +0 -38
  116. package/src/lib/sub-spec-utils.ts +0 -166
  117. package/src/lib/utils.ts +0 -6
  118. package/src/locales/en/common.json +0 -660
  119. package/src/locales/en/errors.json +0 -20
  120. package/src/locales/en/help.json +0 -8
  121. package/src/locales/zh-CN/common.json +0 -660
  122. package/src/locales/zh-CN/errors.json +0 -20
  123. package/src/locales/zh-CN/help.json +0 -8
  124. package/src/main.tsx +0 -12
  125. package/src/pages/ContextPage.tsx +0 -111
  126. package/src/pages/DashboardPage.tsx +0 -97
  127. package/src/pages/DependenciesPage.tsx +0 -881
  128. package/src/pages/ProjectsPage.tsx +0 -432
  129. package/src/pages/SpecDetailPage.tsx +0 -592
  130. package/src/pages/SpecsPage.tsx +0 -319
  131. package/src/pages/StatsPage.tsx +0 -307
  132. package/src/router/projectRoutes.tsx +0 -36
  133. package/src/router.tsx +0 -33
  134. package/src/test/setup.ts +0 -39
  135. package/src/types/api.ts +0 -185
  136. package/tailwind.config.ts +0 -57
  137. package/tsconfig.app.json +0 -29
  138. package/tsconfig.json +0 -7
  139. package/tsconfig.node.json +0 -26
  140. package/tsconfig.tsbuildinfo +0 -1
  141. package/vite.config.ts +0 -27
  142. package/vitest.config.ts +0 -18
  143. /package/{public → dist}/favicon.ico +0 -0
  144. /package/{public → dist}/github-mark-white.svg +0 -0
  145. /package/{public → dist}/github-mark.svg +0 -0
  146. /package/{public → dist}/logo-dark-bg.svg +0 -0
  147. /package/{public → dist}/logo-with-bg.svg +0 -0
  148. /package/{public → dist}/logo.svg +0 -0
  149. /package/{public → dist}/vite.svg +0 -0
@@ -1,225 +0,0 @@
1
- import { useEffect, useMemo, useState } from 'react';
2
- import { useNavigate, useParams } from 'react-router-dom';
3
- import Fuse from 'fuse.js';
4
- import { Clock, FileText, Search, Tag } from 'lucide-react';
5
- import {
6
- CommandDialog,
7
- CommandEmpty,
8
- CommandGroup,
9
- CommandInput,
10
- CommandItem,
11
- CommandList,
12
- CommandSeparator,
13
- Button,
14
- } from '@leanspec/ui-components';
15
- import { StatusBadge } from './StatusBadge';
16
- import { PriorityBadge } from './PriorityBadge';
17
- import { useTranslation } from 'react-i18next';
18
- import type { Spec } from '../types/api';
19
- import { api } from '../lib/api';
20
-
21
- const RECENT_STORAGE_KEY = 'leanspec-recent-searches';
22
-
23
- const formatSpecNumber = (specNumber: number | null) =>
24
- specNumber != null ? specNumber.toString().padStart(3, '0') : null;
25
-
26
- export function QuickSearch() {
27
- const navigate = useNavigate();
28
- const { projectId } = useParams<{ projectId: string }>();
29
- const basePath = projectId ? `/projects/${projectId}` : '/projects/default';
30
- const [open, setOpen] = useState(false);
31
- const [search, setSearch] = useState('');
32
- const [recentSearches, setRecentSearches] = useState<string[]>([]);
33
- const [specs, setSpecs] = useState<Spec[]>([]);
34
- const { t } = useTranslation('common');
35
-
36
- useEffect(() => {
37
- api.getSpecs()
38
- .then((data) => setSpecs(data))
39
- .catch(() => {
40
- // Quick search is best-effort; ignore failures
41
- });
42
- }, []);
43
-
44
- useEffect(() => {
45
- const stored = localStorage.getItem(RECENT_STORAGE_KEY);
46
- if (!stored) return;
47
- try {
48
- const parsed = JSON.parse(stored);
49
- if (Array.isArray(parsed)) {
50
- setRecentSearches(parsed.filter((item) => typeof item === 'string'));
51
- }
52
- } catch {
53
- setRecentSearches([]);
54
- }
55
- }, []);
56
-
57
- useEffect(() => {
58
- const handler = (e: KeyboardEvent) => {
59
- if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
60
- e.preventDefault();
61
- setOpen((prev) => !prev);
62
- }
63
- };
64
-
65
- document.addEventListener('keydown', handler);
66
- return () => document.removeEventListener('keydown', handler);
67
- }, []);
68
-
69
- useEffect(() => {
70
- const handler = (event: Event) => {
71
- const desktopEvent = event as CustomEvent<{ action?: string }>;
72
- if (desktopEvent.detail?.action === 'desktop://menu-find') {
73
- setOpen(true);
74
- }
75
- };
76
-
77
- window.addEventListener('leanspec:desktop-menu', handler as EventListener);
78
- return () => window.removeEventListener('leanspec:desktop-menu', handler as EventListener);
79
- }, []);
80
-
81
- const fuse = useMemo(
82
- () =>
83
- new Fuse(specs, {
84
- keys: [
85
- { name: 'title', weight: 2 },
86
- { name: 'specNumber', weight: 1.5 },
87
- { name: 'specName', weight: 1 },
88
- { name: 'tags', weight: 0.5 },
89
- ],
90
- threshold: 0.4,
91
- includeScore: true,
92
- minMatchCharLength: 2,
93
- }),
94
- [specs]
95
- );
96
-
97
- const results = useMemo(() => {
98
- if (!search) return specs.slice(0, 8);
99
- return fuse.search(search).map((result) => result.item).slice(0, 12);
100
- }, [search, fuse, specs]);
101
-
102
- const tagSuggestions = useMemo(() => {
103
- const allTags = Array.from(new Set(specs.flatMap((s) => s.tags || [])));
104
- if (!search) return allTags.slice(0, 5);
105
- const query = search.toLowerCase();
106
- return allTags
107
- .filter((tag) => tag.toLowerCase().includes(query))
108
- .slice(0, 5);
109
- }, [specs, search]);
110
-
111
- const persistRecentSearches = (entries: string[]) => {
112
- setRecentSearches(entries);
113
- localStorage.setItem(RECENT_STORAGE_KEY, JSON.stringify(entries));
114
- };
115
-
116
- const handleSelect = (spec: Spec) => {
117
- const label = spec.title || spec.specName;
118
- const next = [label, ...recentSearches.filter((item) => item !== label)].slice(0, 5);
119
- persistRecentSearches(next);
120
- setOpen(false);
121
- setSearch('');
122
- navigate(`${basePath}/specs/${spec.specName}`);
123
- };
124
-
125
- const handleTagSelect = (tag: string) => {
126
- navigate(`${basePath}/specs?tag=${encodeURIComponent(tag)}`);
127
- setOpen(false);
128
- setSearch('');
129
- };
130
-
131
- return (
132
- <>
133
- <Button
134
- onClick={() => setOpen(true)}
135
- variant="outline"
136
- size="sm"
137
- className="gap-2 text-muted-foreground hover:text-foreground"
138
- aria-label={t('quickSearch.open')}
139
- >
140
- <Search className="h-4 w-4" />
141
- <span className="hidden sm:inline">{t('quickSearch.button')}</span>
142
- <kbd className="hidden md:inline-flex pointer-events-none h-5 select-none items-center gap-1 rounded border bg-muted px-1.5 font-mono text-[10px] font-medium opacity-100">
143
- <span className="text-xs">⌘</span>K
144
- </kbd>
145
- </Button>
146
-
147
- <CommandDialog
148
- open={open}
149
- onOpenChange={(value) => {
150
- setOpen(value);
151
- if (!value) setSearch('');
152
- }}
153
- >
154
- <CommandInput
155
- placeholder={t('quickSearch.placeholder')}
156
- value={search}
157
- onValueChange={setSearch}
158
- />
159
- <CommandList>
160
- <CommandEmpty>{t('search.noResults')}</CommandEmpty>
161
-
162
- {!search && recentSearches.length > 0 && (
163
- <CommandGroup heading={t('quickSearch.recentSearches')}>
164
- {recentSearches.map((recent) => (
165
- <CommandItem
166
- key={recent}
167
- value={recent}
168
- onSelect={() => setSearch(recent)}
169
- >
170
- <Clock className="mr-2 h-4 w-4" />
171
- {recent}
172
- </CommandItem>
173
- ))}
174
- </CommandGroup>
175
- )}
176
-
177
- <CommandGroup heading={t('spec.specs')}>
178
- {results.map((spec) => {
179
- const specNumber = formatSpecNumber(spec.specNumber ?? null);
180
- const label = spec.title || spec.specName;
181
- return (
182
- <CommandItem
183
- key={spec.specName}
184
- value={`${specNumber ? `#${specNumber}` : ''} ${label}`.trim()}
185
- onSelect={() => handleSelect(spec)}
186
- >
187
- <FileText className="mr-2 h-4 w-4" />
188
- <div className="flex-1 flex items-center justify-between gap-2 min-w-0">
189
- <div className="flex-1 min-w-0">
190
- <div className="flex items-center gap-2">
191
- {specNumber && (
192
- <span className="text-xs font-mono text-muted-foreground">#{specNumber}</span>
193
- )}
194
- <span className="truncate font-medium">{label}</span>
195
- </div>
196
- <div className="text-xs text-muted-foreground truncate">{spec.specName}</div>
197
- </div>
198
- <div className="flex items-center gap-1 shrink-0">
199
- {spec.status && <StatusBadge status={spec.status} />}
200
- {spec.priority && <PriorityBadge priority={spec.priority} />}
201
- </div>
202
- </div>
203
- </CommandItem>
204
- );
205
- })}
206
- </CommandGroup>
207
-
208
- {tagSuggestions.length > 0 && (
209
- <>
210
- <CommandSeparator />
211
- <CommandGroup heading={t('quickSearch.filterHeading')}>
212
- {tagSuggestions.map((tag) => (
213
- <CommandItem key={tag} value={tag} onSelect={() => handleTagSelect(tag)}>
214
- <Tag className="mr-2 h-4 w-4" />
215
- <span className="font-medium">{tag}</span>
216
- </CommandItem>
217
- ))}
218
- </CommandGroup>
219
- </>
220
- )}
221
- </CommandList>
222
- </CommandDialog>
223
- </>
224
- );
225
- }
@@ -1,40 +0,0 @@
1
- import { Navigate } from 'react-router-dom';
2
- import { useProject } from '../contexts';
3
-
4
- const STORAGE_KEY = 'leanspec-current-project';
5
-
6
- /**
7
- * Root redirect component that navigates to the appropriate page:
8
- * - If a project is stored in localStorage and exists, navigate to that project's dashboard
9
- * - Otherwise, navigate to the projects list page
10
- */
11
- export function RootRedirect() {
12
- const { currentProject, projects, loading } = useProject();
13
-
14
- if (loading) {
15
- // Show nothing while loading to avoid flash of redirect
16
- return null;
17
- }
18
-
19
- // If we have a current project, navigate to it
20
- if (currentProject?.id) {
21
- return <Navigate to={`/projects/${currentProject.id}`} replace />;
22
- }
23
-
24
- // Check if there's a stored project ID
25
- const storedId = localStorage.getItem(STORAGE_KEY);
26
- if (storedId) {
27
- const storedProject = projects.find((p) => p.id === storedId);
28
- if (storedProject) {
29
- return <Navigate to={`/projects/${storedProject.id}`} replace />;
30
- }
31
- }
32
-
33
- // If there are any projects, navigate to the first one
34
- if (projects.length > 0) {
35
- return <Navigate to={`/projects/${projects[0].id}`} replace />;
36
- }
37
-
38
- // No projects, go to projects page
39
- return <Navigate to="/projects" replace />;
40
- }
@@ -1,10 +0,0 @@
1
- import { useOutletContext } from 'react-router-dom';
2
-
3
- export type SpecDetailLayoutContext = {
4
- mobileOpen: boolean;
5
- setMobileOpen: (open: boolean) => void;
6
- };
7
-
8
- export function useSpecDetailLayoutContext() {
9
- return useOutletContext<SpecDetailLayoutContext>();
10
- }
@@ -1,14 +0,0 @@
1
- import { useState } from 'react';
2
- import { Outlet } from 'react-router-dom';
3
- import { SpecsNavSidebar } from './SpecsNavSidebar';
4
-
5
- export function SpecDetailLayout() {
6
- const [mobileOpen, setMobileOpen] = useState(false);
7
-
8
- return (
9
- <div className="flex h-full relative">
10
- <SpecsNavSidebar mobileOpen={mobileOpen} onMobileOpenChange={setMobileOpen} />
11
- <Outlet context={{ mobileOpen, setMobileOpen }} />
12
- </div>
13
- );
14
- }