@leanspec/ui 0.2.14 → 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 (150) 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 +12 -2
  52. package/eslint.config.js +0 -23
  53. package/package.json.backup +0 -83
  54. package/postcss.config.js +0 -6
  55. package/src/App.css +0 -42
  56. package/src/App.tsx +0 -17
  57. package/src/assets/react.svg +0 -1
  58. package/src/components/LanguageSwitcher.tsx +0 -67
  59. package/src/components/Layout.tsx +0 -88
  60. package/src/components/MainSidebar.tsx +0 -163
  61. package/src/components/MermaidDiagram.tsx +0 -85
  62. package/src/components/MinimalLayout.tsx +0 -51
  63. package/src/components/Navigation.tsx +0 -254
  64. package/src/components/PriorityBadge.tsx +0 -59
  65. package/src/components/ProjectSwitcher.tsx +0 -222
  66. package/src/components/QuickSearch.tsx +0 -225
  67. package/src/components/RootRedirect.tsx +0 -40
  68. package/src/components/SpecDetailLayout.context.ts +0 -10
  69. package/src/components/SpecDetailLayout.tsx +0 -14
  70. package/src/components/SpecsNavSidebar.tsx +0 -615
  71. package/src/components/StatusBadge.tsx +0 -59
  72. package/src/components/ThemeToggle.tsx +0 -25
  73. package/src/components/Tooltip.tsx +0 -29
  74. package/src/components/context/ContextClient.tsx +0 -471
  75. package/src/components/context/ContextFileDetail.tsx +0 -163
  76. package/src/components/dashboard/ActivityItem.tsx +0 -36
  77. package/src/components/dashboard/DashboardClient.tsx +0 -218
  78. package/src/components/dashboard/SpecListItem.tsx +0 -58
  79. package/src/components/dashboard/StatCard.tsx +0 -52
  80. package/src/components/dependencies/SpecNode.tsx +0 -128
  81. package/src/components/dependencies/SpecSidebar.tsx +0 -256
  82. package/src/components/dependencies/constants.ts +0 -25
  83. package/src/components/dependencies/types.ts +0 -38
  84. package/src/components/dependencies/utils.ts +0 -261
  85. package/src/components/metadata-editors/PriorityEditor.tsx +0 -89
  86. package/src/components/metadata-editors/StatusEditor.tsx +0 -85
  87. package/src/components/metadata-editors/TagsEditor.tsx +0 -207
  88. package/src/components/projects/CreateProjectDialog.tsx +0 -162
  89. package/src/components/projects/DirectoryPicker.tsx +0 -182
  90. package/src/components/shared/BackToTop.tsx +0 -39
  91. package/src/components/shared/ColorPicker.tsx +0 -68
  92. package/src/components/shared/EmptyState.tsx +0 -35
  93. package/src/components/shared/ErrorBoundary.tsx +0 -79
  94. package/src/components/shared/PageHeader.tsx +0 -23
  95. package/src/components/shared/PageTransition.tsx +0 -40
  96. package/src/components/shared/ProjectAvatar.tsx +0 -107
  97. package/src/components/shared/Skeletons.tsx +0 -184
  98. package/src/components/spec-detail/EditableMetadata.tsx +0 -129
  99. package/src/components/spec-detail/MarkdownRenderer.tsx +0 -47
  100. package/src/components/spec-detail/TableOfContents.tsx +0 -150
  101. package/src/components/specs/BoardView.tsx +0 -204
  102. package/src/components/specs/ListView.tsx +0 -62
  103. package/src/components/specs/SpecsFilters.tsx +0 -190
  104. package/src/contexts/KeyboardShortcutsContext.tsx +0 -95
  105. package/src/contexts/LayoutContext.tsx +0 -45
  106. package/src/contexts/ProjectContext.tsx +0 -163
  107. package/src/contexts/ThemeContext.tsx +0 -90
  108. package/src/contexts/index.ts +0 -7
  109. package/src/hooks/useKeyboardShortcuts.ts +0 -87
  110. package/src/index.css +0 -624
  111. package/src/lib/api.ts +0 -72
  112. package/src/lib/backend-adapter.ts +0 -382
  113. package/src/lib/date-utils.ts +0 -122
  114. package/src/lib/i18n.test.ts +0 -57
  115. package/src/lib/i18n.ts +0 -51
  116. package/src/lib/markdown-utils.ts +0 -38
  117. package/src/lib/sub-spec-utils.ts +0 -166
  118. package/src/lib/utils.ts +0 -6
  119. package/src/locales/en/common.json +0 -660
  120. package/src/locales/en/errors.json +0 -20
  121. package/src/locales/en/help.json +0 -8
  122. package/src/locales/zh-CN/common.json +0 -660
  123. package/src/locales/zh-CN/errors.json +0 -20
  124. package/src/locales/zh-CN/help.json +0 -8
  125. package/src/main.tsx +0 -12
  126. package/src/pages/ContextPage.tsx +0 -111
  127. package/src/pages/DashboardPage.tsx +0 -97
  128. package/src/pages/DependenciesPage.tsx +0 -881
  129. package/src/pages/ProjectsPage.tsx +0 -432
  130. package/src/pages/SpecDetailPage.tsx +0 -592
  131. package/src/pages/SpecsPage.tsx +0 -319
  132. package/src/pages/StatsPage.tsx +0 -307
  133. package/src/router/projectRoutes.tsx +0 -36
  134. package/src/router.tsx +0 -33
  135. package/src/test/setup.ts +0 -39
  136. package/src/types/api.ts +0 -185
  137. package/tailwind.config.ts +0 -57
  138. package/tsconfig.app.json +0 -29
  139. package/tsconfig.json +0 -7
  140. package/tsconfig.node.json +0 -26
  141. package/tsconfig.tsbuildinfo +0 -1
  142. package/vite.config.ts +0 -27
  143. package/vitest.config.ts +0 -18
  144. /package/{public → dist}/favicon.ico +0 -0
  145. /package/{public → dist}/github-mark-white.svg +0 -0
  146. /package/{public → dist}/github-mark.svg +0 -0
  147. /package/{public → dist}/logo-dark-bg.svg +0 -0
  148. /package/{public → dist}/logo-with-bg.svg +0 -0
  149. /package/{public → dist}/logo.svg +0 -0
  150. /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
- }