@lon-ask/dockit 0.1.1 → 0.1.4
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/LICENSE +21 -674
- package/README.md +618 -328
- package/SKILL.md +61 -110
- package/apps/client/dist/assets/{index-CqOXxsEZ.js → index-DzadxeQH.js} +2 -2
- package/apps/client/dist/index.html +1 -1
- package/apps/client/index.html +12 -0
- package/apps/client/package.json +26 -0
- package/apps/client/src/App.tsx +18 -0
- package/apps/client/src/api/client.ts +54 -0
- package/apps/client/src/components/BuildPanel.tsx +77 -0
- package/apps/client/src/components/DocViewer.tsx +76 -0
- package/apps/client/src/components/EntryDetail.tsx +322 -0
- package/apps/client/src/components/EntryForm.tsx +117 -0
- package/apps/client/src/components/EntryList.tsx +165 -0
- package/apps/client/src/components/GlobalSearchBar.tsx +166 -0
- package/apps/client/src/components/Layout.tsx +57 -0
- package/apps/client/src/components/SearchBar.tsx +103 -0
- package/apps/client/src/components/SourceForm.tsx +497 -0
- package/apps/client/src/hooks/useTheme.ts +19 -0
- package/apps/client/src/index.css +77 -0
- package/apps/client/src/main.tsx +13 -0
- package/apps/client/src/types.ts +105 -0
- package/apps/client/vite.config.ts +13 -0
- package/apps/server/dist/core/domain/entry.js +20 -0
- package/apps/server/dist/core/domain/entry.js.map +1 -0
- package/apps/server/dist/core/domain/errors.js +33 -0
- package/apps/server/dist/core/domain/errors.js.map +1 -0
- package/apps/server/dist/core/domain/knowledge-graph.js +2 -0
- package/apps/server/dist/core/domain/knowledge-graph.js.map +1 -0
- package/apps/server/dist/core/domain/types.js +2 -0
- package/apps/server/dist/core/domain/types.js.map +1 -0
- package/apps/server/dist/core/ports/IBuildRepository.js +2 -0
- package/apps/server/dist/core/ports/IBuildRepository.js.map +1 -0
- package/apps/server/dist/core/ports/IDocumentNormalizer.js +2 -0
- package/apps/server/dist/core/ports/IDocumentNormalizer.js.map +1 -0
- package/apps/server/dist/core/ports/IDocumentStore.js +2 -0
- package/apps/server/dist/core/ports/IDocumentStore.js.map +1 -0
- package/apps/server/dist/core/ports/IEntryReadModel.js +2 -0
- package/apps/server/dist/core/ports/IEntryReadModel.js.map +1 -0
- package/apps/server/dist/core/ports/IEntryRepository.js +2 -0
- package/apps/server/dist/core/ports/IEntryRepository.js.map +1 -0
- package/apps/server/dist/core/ports/IKnowledgeGraph.js +2 -0
- package/apps/server/dist/core/ports/IKnowledgeGraph.js.map +1 -0
- package/apps/server/dist/core/ports/IPathResolver.js +2 -0
- package/apps/server/dist/core/ports/IPathResolver.js.map +1 -0
- package/apps/server/dist/core/ports/ISearchEngine.js +2 -0
- package/apps/server/dist/core/ports/ISearchEngine.js.map +1 -0
- package/apps/server/dist/core/ports/ISourceProcessor.js +2 -0
- package/apps/server/dist/core/ports/ISourceProcessor.js.map +1 -0
- package/apps/server/dist/core/ports/ISourceRepository.js +2 -0
- package/apps/server/dist/core/ports/ISourceRepository.js.map +1 -0
- package/apps/server/dist/core/usecases/BuildUseCase.js +76 -0
- package/apps/server/dist/core/usecases/BuildUseCase.js.map +1 -0
- package/apps/server/dist/core/usecases/ConfigUseCase.js +62 -0
- package/apps/server/dist/core/usecases/ConfigUseCase.js.map +1 -0
- package/apps/server/dist/core/usecases/SearchUseCase.js +17 -0
- package/apps/server/dist/core/usecases/SearchUseCase.js.map +1 -0
- package/apps/server/dist/index.js +86 -0
- package/apps/server/dist/index.js.map +1 -0
- package/apps/server/dist/infrastructure/filesystem/FileSystemDocumentStore.js +25 -0
- package/apps/server/dist/infrastructure/filesystem/FileSystemDocumentStore.js.map +1 -0
- package/apps/server/dist/infrastructure/graph/GraphSearchDecorator.js +42 -0
- package/apps/server/dist/infrastructure/graph/GraphSearchDecorator.js.map +1 -0
- package/apps/server/dist/infrastructure/graph/GraphifyKnowledgeGraph.js +145 -0
- package/apps/server/dist/infrastructure/graph/GraphifyKnowledgeGraph.js.map +1 -0
- package/apps/server/dist/infrastructure/graph/index.js +3 -0
- package/apps/server/dist/infrastructure/graph/index.js.map +1 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/SqliteBuildRepository.js +21 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/SqliteBuildRepository.js.map +1 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/SqliteEntryReadModel.js +11 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/SqliteEntryReadModel.js.map +1 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/SqliteEntryRepository.js +59 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/SqliteEntryRepository.js.map +1 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/SqliteSourceRepository.js +47 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/SqliteSourceRepository.js.map +1 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/connection.js +50 -0
- package/apps/server/dist/infrastructure/persistence/sqlite/connection.js.map +1 -0
- package/apps/server/dist/infrastructure/search/SearchEngineFactory.js +32 -0
- package/apps/server/dist/infrastructure/search/SearchEngineFactory.js.map +1 -0
- package/apps/server/dist/infrastructure/search/json/JsonSearchEngine.js +147 -0
- package/apps/server/dist/infrastructure/search/json/JsonSearchEngine.js.map +1 -0
- package/apps/server/dist/infrastructure/search/vector/EmbeddingService.js +23 -0
- package/apps/server/dist/infrastructure/search/vector/EmbeddingService.js.map +1 -0
- package/apps/server/dist/infrastructure/search/vector/VectorSearchEngine.js +378 -0
- package/apps/server/dist/infrastructure/search/vector/VectorSearchEngine.js.map +1 -0
- package/apps/server/dist/infrastructure/source-processors/AntoraSourceProcessor.js +11 -0
- package/apps/server/dist/infrastructure/source-processors/AntoraSourceProcessor.js.map +1 -0
- package/apps/server/dist/infrastructure/source-processors/AsciidocSourceProcessor.js +9 -0
- package/apps/server/dist/infrastructure/source-processors/AsciidocSourceProcessor.js.map +1 -0
- package/apps/server/dist/infrastructure/source-processors/DocumentNormalizer.js +11 -0
- package/apps/server/dist/infrastructure/source-processors/DocumentNormalizer.js.map +1 -0
- package/apps/server/dist/infrastructure/source-processors/GithubMarkdownSourceProcessor.js +9 -0
- package/apps/server/dist/infrastructure/source-processors/GithubMarkdownSourceProcessor.js.map +1 -0
- package/apps/server/dist/infrastructure/source-processors/MavenSourceProcessor.js +9 -0
- package/apps/server/dist/infrastructure/source-processors/MavenSourceProcessor.js.map +1 -0
- package/apps/server/dist/infrastructure/source-processors/PathResolver.js +5 -0
- package/apps/server/dist/infrastructure/source-processors/PathResolver.js.map +1 -0
- package/apps/server/dist/infrastructure/source-processors/SourceCodeSourceProcessor.js +269 -0
- package/apps/server/dist/infrastructure/source-processors/SourceCodeSourceProcessor.js.map +1 -0
- package/apps/server/dist/infrastructure/source-processors/ZipSourceProcessor.js +9 -0
- package/apps/server/dist/infrastructure/source-processors/ZipSourceProcessor.js.map +1 -0
- package/apps/server/dist/mcp-http.js +93 -0
- package/apps/server/dist/mcp-http.js.map +1 -0
- package/apps/server/dist/mcp.js +339 -0
- package/apps/server/dist/mcp.js.map +1 -0
- package/apps/server/dist/routes/build.js +89 -0
- package/apps/server/dist/routes/build.js.map +1 -0
- package/apps/server/dist/routes/entries.js +52 -0
- package/apps/server/dist/routes/entries.js.map +1 -0
- package/apps/server/dist/routes/graph.js +58 -0
- package/apps/server/dist/routes/graph.js.map +1 -0
- package/apps/server/dist/routes/search.js +24 -0
- package/apps/server/dist/routes/search.js.map +1 -0
- package/apps/server/dist/routes/sources.js +100 -0
- package/apps/server/dist/routes/sources.js.map +1 -0
- package/apps/server/dist/routes/viewer.js +22 -0
- package/apps/server/dist/routes/viewer.js.map +1 -0
- package/apps/server/dist/services/antora.js +222 -0
- package/apps/server/dist/services/antora.js.map +1 -0
- package/apps/server/dist/services/asciidoc.js +206 -0
- package/apps/server/dist/services/asciidoc.js.map +1 -0
- package/apps/server/dist/services/configLoader.js +150 -0
- package/apps/server/dist/services/configLoader.js.map +1 -0
- package/apps/server/dist/services/githubMarkdown.js +221 -0
- package/apps/server/dist/services/githubMarkdown.js.map +1 -0
- package/apps/server/dist/services/maven.js +148 -0
- package/apps/server/dist/services/maven.js.map +1 -0
- package/apps/server/dist/services/normalizer.js +42 -0
- package/apps/server/dist/services/normalizer.js.map +1 -0
- package/apps/server/dist/services/paths.js +5 -0
- package/apps/server/dist/services/paths.js.map +1 -0
- package/apps/server/dist/services/textExtractor.js +46 -0
- package/apps/server/dist/services/textExtractor.js.map +1 -0
- package/apps/server/dist/services/zip.js +63 -0
- package/apps/server/dist/services/zip.js.map +1 -0
- package/apps/server/package.json +38 -0
- package/apps/server/src/infrastructure/source-processors/SourceCodeSourceProcessor.ts +9 -2
- package/bin/commands/dev.ts +2 -2
- package/bin/commands/serve.ts +2 -2
- package/package.json +22 -4
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import type { SourceType, SourceConfig } from '../types';
|
|
3
|
+
|
|
4
|
+
interface Props {
|
|
5
|
+
open: boolean;
|
|
6
|
+
onClose: () => void;
|
|
7
|
+
onCreate: (data: { type: SourceType; label: string; config: SourceConfig }) => Promise<void>;
|
|
8
|
+
initial?: { type: SourceType; label: string; config: SourceConfig };
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const TYPE_LABELS: Record<SourceType, string> = {
|
|
12
|
+
zip: 'ZIP Bundle',
|
|
13
|
+
antora: 'Antora',
|
|
14
|
+
maven: 'Maven',
|
|
15
|
+
asciidoc: 'AsciiDoc',
|
|
16
|
+
'github-markdown': 'GitHub Markdown',
|
|
17
|
+
'source-code': 'Source Code',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type ZipMode = 'remote' | 'local';
|
|
21
|
+
type MavenMode = 'direct' | 'cli' | 'localJar';
|
|
22
|
+
type RepoMode = 'git' | 'localDir' | 'zipFile';
|
|
23
|
+
|
|
24
|
+
export default function SourceForm({ open, onClose, onCreate, initial }: Props) {
|
|
25
|
+
const [type, setType] = useState<SourceType>(initial?.type || 'zip');
|
|
26
|
+
const [label, setLabel] = useState(initial?.label || '');
|
|
27
|
+
const [saving, setSaving] = useState(false);
|
|
28
|
+
const [error, setError] = useState<string | null>(null);
|
|
29
|
+
|
|
30
|
+
function getInitialMode(): ZipMode {
|
|
31
|
+
if (initial?.config && (initial.config as Record<string, unknown>).localPath) return 'local';
|
|
32
|
+
return 'remote';
|
|
33
|
+
}
|
|
34
|
+
function getInitialMavenMode(): MavenMode {
|
|
35
|
+
if (initial?.config && (initial.config as Record<string, unknown>).localJar) return 'localJar';
|
|
36
|
+
if (initial?.config && (initial.config as Record<string, unknown>).useMavenCommand) return 'cli';
|
|
37
|
+
return 'direct';
|
|
38
|
+
}
|
|
39
|
+
function getInitialRepoMode(): RepoMode {
|
|
40
|
+
const c = initial?.config as Record<string, unknown> | undefined;
|
|
41
|
+
if (c?.localPath) return 'localDir';
|
|
42
|
+
if (c?.zipPath && !c?.repoUrl) return 'zipFile';
|
|
43
|
+
return 'git';
|
|
44
|
+
}
|
|
45
|
+
function getInitialGhmdMode(): RepoMode {
|
|
46
|
+
const c = initial?.config as Record<string, unknown> | undefined;
|
|
47
|
+
if (c?.localPath) return 'localDir';
|
|
48
|
+
return 'git';
|
|
49
|
+
}
|
|
50
|
+
function getInitialScMode(): RepoMode {
|
|
51
|
+
const c = initial?.config as Record<string, unknown> | undefined;
|
|
52
|
+
if (c?.localPath) return 'localDir';
|
|
53
|
+
if (c?.zipPath && !c?.repoUrl) return 'zipFile';
|
|
54
|
+
return 'git';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const [zipMode, setZipMode] = useState<ZipMode>(getInitialMode());
|
|
58
|
+
const [mavenMode, setMavenMode] = useState<MavenMode>(getInitialMavenMode());
|
|
59
|
+
const [antoraMode, setAntoraMode] = useState<RepoMode>(getInitialRepoMode());
|
|
60
|
+
const [adocMode, setAdocMode] = useState<RepoMode>(getInitialRepoMode());
|
|
61
|
+
const [ghmdMode, setGhmdMode] = useState<RepoMode>(getInitialGhmdMode());
|
|
62
|
+
const [scMode, setScMode] = useState<RepoMode>(getInitialScMode());
|
|
63
|
+
|
|
64
|
+
const [zipUrl, setZipUrl] = useState((initial?.config as { url?: string })?.url || '');
|
|
65
|
+
const [zipLocalPath, setZipLocalPath] = useState((initial?.config as { localPath?: string })?.localPath || '');
|
|
66
|
+
const [antoraRepoUrl, setAntoraRepoUrl] = useState((initial?.config as { repoUrl?: string })?.repoUrl || '');
|
|
67
|
+
const [antoraZipPath, setAntoraZipPath] = useState((initial?.config as { zipPath?: string })?.zipPath || '');
|
|
68
|
+
const [antoraLocalPath, setAntoraLocalPath] = useState((initial?.config as { localPath?: string })?.localPath || '');
|
|
69
|
+
const [mavenGroupId, setMavenGroupId] = useState((initial?.config as { groupId?: string })?.groupId || '');
|
|
70
|
+
const [mavenArtifactId, setMavenArtifactId] = useState((initial?.config as { artifactId?: string })?.artifactId || '');
|
|
71
|
+
const [mavenVersion, setMavenVersion] = useState((initial?.config as { version?: string })?.version || '');
|
|
72
|
+
const [mavenClassifier, setMavenClassifier] = useState((initial?.config as { classifier?: string })?.classifier || 'javadoc');
|
|
73
|
+
const [mavenLocalJar, setMavenLocalJar] = useState((initial?.config as { localJar?: string })?.localJar || '');
|
|
74
|
+
const [adocRepoUrl, setAdocRepoUrl] = useState((initial?.config as { repoUrl?: string })?.repoUrl || '');
|
|
75
|
+
const [adocSourcePath, setAdocSourcePath] = useState((initial?.config as { sourcePath?: string })?.sourcePath || '');
|
|
76
|
+
const [adocZipPath, setAdocZipPath] = useState((initial?.config as { zipPath?: string })?.zipPath || '');
|
|
77
|
+
const [adocLocalPath, setAdocLocalPath] = useState((initial?.config as { localPath?: string })?.localPath || '');
|
|
78
|
+
const [ghmdRepoUrl, setGhmdRepoUrl] = useState((initial?.config as { repoUrl?: string })?.repoUrl || '');
|
|
79
|
+
const [ghmdLocalPath, setGhmdLocalPath] = useState((initial?.config as { localPath?: string })?.localPath || '');
|
|
80
|
+
const [ghmdSourcePath, setGhmdSourcePath] = useState((initial?.config as { sourcePath?: string })?.sourcePath || '');
|
|
81
|
+
const [ghmdBranch, setGhmdBranch] = useState((initial?.config as { branch?: string })?.branch || '');
|
|
82
|
+
const [graphifyEnabled, setGraphifyEnabled] = useState((initial?.config as { graphifyEnabled?: boolean })?.graphifyEnabled || false);
|
|
83
|
+
const [graphifySourcePath, setGraphifySourcePath] = useState((initial?.config as { graphifySourcePath?: string })?.graphifySourcePath || '');
|
|
84
|
+
const [scRepoUrl, setScRepoUrl] = useState((initial?.config as { repoUrl?: string })?.repoUrl || '');
|
|
85
|
+
const [scLocalPath, setScLocalPath] = useState((initial?.config as { localPath?: string })?.localPath || '');
|
|
86
|
+
const [scZipPath, setScZipPath] = useState((initial?.config as { zipPath?: string })?.zipPath || '');
|
|
87
|
+
const [scSourcePath, setScSourcePath] = useState((initial?.config as { sourcePath?: string })?.sourcePath || '');
|
|
88
|
+
const [scBranch, setScBranch] = useState((initial?.config as { branch?: string })?.branch || '');
|
|
89
|
+
|
|
90
|
+
if (!open) return null;
|
|
91
|
+
|
|
92
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
93
|
+
e.preventDefault();
|
|
94
|
+
setError(null);
|
|
95
|
+
|
|
96
|
+
let config: SourceConfig;
|
|
97
|
+
switch (type) {
|
|
98
|
+
case 'zip':
|
|
99
|
+
if (zipMode === 'remote') {
|
|
100
|
+
if (!zipUrl.trim()) { setError('URL is required'); return; }
|
|
101
|
+
config = { url: zipUrl.trim() };
|
|
102
|
+
} else {
|
|
103
|
+
if (!zipLocalPath.trim()) { setError('Local path is required'); return; }
|
|
104
|
+
config = { localPath: zipLocalPath.trim() };
|
|
105
|
+
}
|
|
106
|
+
break;
|
|
107
|
+
case 'antora':
|
|
108
|
+
if (antoraMode === 'git') {
|
|
109
|
+
if (!antoraRepoUrl.trim()) { setError('Repository URL is required'); return; }
|
|
110
|
+
config = { repoUrl: antoraRepoUrl.trim(), graphifyEnabled, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
111
|
+
} else if (antoraMode === 'localDir') {
|
|
112
|
+
if (!antoraLocalPath.trim()) { setError('Local path is required'); return; }
|
|
113
|
+
config = { localPath: antoraLocalPath.trim(), graphifyEnabled, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
114
|
+
} else {
|
|
115
|
+
if (!antoraZipPath.trim()) { setError('ZIP path is required'); return; }
|
|
116
|
+
config = { zipPath: antoraZipPath.trim(), graphifyEnabled, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
117
|
+
}
|
|
118
|
+
break;
|
|
119
|
+
case 'maven':
|
|
120
|
+
if (!mavenGroupId.trim() || !mavenArtifactId.trim() || !mavenVersion.trim()) {
|
|
121
|
+
setError('Group ID, Artifact ID, and Version are required');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (mavenMode === 'direct') {
|
|
125
|
+
config = { groupId: mavenGroupId.trim(), artifactId: mavenArtifactId.trim(), version: mavenVersion.trim(), classifier: mavenClassifier.trim() || 'javadoc' };
|
|
126
|
+
} else if (mavenMode === 'cli') {
|
|
127
|
+
config = { groupId: mavenGroupId.trim(), artifactId: mavenArtifactId.trim(), version: mavenVersion.trim(), classifier: mavenClassifier.trim() || 'javadoc', useMavenCommand: true };
|
|
128
|
+
} else {
|
|
129
|
+
if (!mavenLocalJar.trim()) { setError('Local JAR path is required'); return; }
|
|
130
|
+
config = { groupId: mavenGroupId.trim(), artifactId: mavenArtifactId.trim(), version: mavenVersion.trim(), classifier: mavenClassifier.trim() || 'javadoc', localJar: mavenLocalJar.trim() };
|
|
131
|
+
}
|
|
132
|
+
break;
|
|
133
|
+
case 'asciidoc':
|
|
134
|
+
if (adocMode === 'git') {
|
|
135
|
+
if (!adocRepoUrl.trim()) { setError('Repository URL is required'); return; }
|
|
136
|
+
config = { repoUrl: adocRepoUrl.trim(), sourcePath: adocSourcePath.trim() || undefined, graphifyEnabled, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
137
|
+
} else if (adocMode === 'localDir') {
|
|
138
|
+
if (!adocLocalPath.trim()) { setError('Local path is required'); return; }
|
|
139
|
+
config = { localPath: adocLocalPath.trim(), sourcePath: adocSourcePath.trim() || undefined, graphifyEnabled, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
140
|
+
} else {
|
|
141
|
+
config = { zipPath: adocZipPath.trim(), sourcePath: adocSourcePath.trim() || undefined, graphifyEnabled, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
142
|
+
}
|
|
143
|
+
break;
|
|
144
|
+
case 'github-markdown':
|
|
145
|
+
if (ghmdMode === 'git') {
|
|
146
|
+
if (!ghmdRepoUrl.trim()) { setError('Repository URL is required'); return; }
|
|
147
|
+
config = { repoUrl: ghmdRepoUrl.trim(), sourcePath: ghmdSourcePath.trim() || undefined, branch: ghmdBranch.trim() || undefined, graphifyEnabled, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
148
|
+
} else {
|
|
149
|
+
if (!ghmdLocalPath.trim()) { setError('Local path is required'); return; }
|
|
150
|
+
config = { localPath: ghmdLocalPath.trim(), sourcePath: ghmdSourcePath.trim() || undefined, graphifyEnabled, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
case 'source-code':
|
|
154
|
+
if (scMode === 'git') {
|
|
155
|
+
if (!scRepoUrl.trim()) { setError('Repository URL is required'); return; }
|
|
156
|
+
config = { repoUrl: scRepoUrl.trim(), sourcePath: scSourcePath.trim() || undefined, branch: scBranch.trim() || undefined, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
157
|
+
} else if (scMode === 'localDir') {
|
|
158
|
+
if (!scLocalPath.trim()) { setError('Local path is required'); return; }
|
|
159
|
+
config = { localPath: scLocalPath.trim(), sourcePath: scSourcePath.trim() || undefined, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
160
|
+
} else {
|
|
161
|
+
if (!scZipPath.trim()) { setError('ZIP file path is required'); return; }
|
|
162
|
+
config = { zipPath: scZipPath.trim(), sourcePath: scSourcePath.trim() || undefined, graphifySourcePath: graphifySourcePath.trim() || undefined };
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
setSaving(true);
|
|
168
|
+
try {
|
|
169
|
+
await onCreate({ type, label: label.trim() || `${TYPE_LABELS[type]} source`, config });
|
|
170
|
+
onClose();
|
|
171
|
+
} catch (err) {
|
|
172
|
+
setError((err as Error).message);
|
|
173
|
+
} finally {
|
|
174
|
+
setSaving(false);
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const inputClass = "w-full px-3.5 py-2.5 bg-bg ring-1 ring-border rounded-lg text-sm text-text placeholder:text-text-muted focus:outline-none focus:ring-2 focus:ring-primary/30 focus:border-primary transition-all";
|
|
179
|
+
const labelClass = "block text-sm font-medium text-text mb-2";
|
|
180
|
+
const helpClass = "text-xs text-text-muted mt-1.5";
|
|
181
|
+
|
|
182
|
+
const modeBtnClass = (active: boolean) =>
|
|
183
|
+
`flex-1 px-2.5 py-2 rounded-lg text-xs font-medium ring-1 transition-all cursor-pointer text-center ${
|
|
184
|
+
active
|
|
185
|
+
? 'ring-primary bg-primary/10 text-primary'
|
|
186
|
+
: 'ring-border text-text-dim hover:ring-text-muted hover:bg-bg-alt'
|
|
187
|
+
}`;
|
|
188
|
+
|
|
189
|
+
return (
|
|
190
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
|
191
|
+
<div className="absolute inset-0 bg-black/40 backdrop-blur-sm" onClick={onClose} />
|
|
192
|
+
<div className="relative bg-surface ring-1 ring-border rounded-2xl shadow-2xl w-full max-w-md p-6 max-h-[90vh] overflow-auto">
|
|
193
|
+
<h2 className="text-lg font-semibold text-text mb-1">
|
|
194
|
+
{initial ? 'Edit Source' : 'Add Source'}
|
|
195
|
+
</h2>
|
|
196
|
+
<p className="text-sm text-text-dim mb-5">
|
|
197
|
+
Select the source type and fill in the details.
|
|
198
|
+
</p>
|
|
199
|
+
|
|
200
|
+
<form onSubmit={handleSubmit} className="space-y-5">
|
|
201
|
+
<div>
|
|
202
|
+
<label className={labelClass}>Type</label>
|
|
203
|
+
<div className="flex gap-2 flex-wrap">
|
|
204
|
+
{(['zip', 'maven', 'antora', 'asciidoc', 'github-markdown', 'source-code'] as SourceType[]).map((t) => (
|
|
205
|
+
<button key={t} type="button" onClick={() => setType(t)}
|
|
206
|
+
className={modeBtnClass(type === t)}>
|
|
207
|
+
{TYPE_LABELS[t]}
|
|
208
|
+
</button>
|
|
209
|
+
))}
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
|
|
213
|
+
<div>
|
|
214
|
+
<label className={labelClass}>Label</label>
|
|
215
|
+
<input type="text" value={label} onChange={(e) => setLabel(e.target.value)}
|
|
216
|
+
placeholder="e.g. API Docs" className={inputClass} />
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
{type === 'zip' && (
|
|
220
|
+
<div className="space-y-3">
|
|
221
|
+
<div>
|
|
222
|
+
<label className={labelClass}>Source</label>
|
|
223
|
+
<div className="flex gap-2">
|
|
224
|
+
<button type="button" onClick={() => setZipMode('remote')} className={modeBtnClass(zipMode === 'remote')}>Remote URL</button>
|
|
225
|
+
<button type="button" onClick={() => setZipMode('local')} className={modeBtnClass(zipMode === 'local')}>Local File</button>
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
{zipMode === 'remote' ? (
|
|
229
|
+
<div>
|
|
230
|
+
<label className={labelClass}>ZIP URL</label>
|
|
231
|
+
<input type="url" value={zipUrl} onChange={(e) => setZipUrl(e.target.value)} placeholder="https://example.com/docs.zip" className={inputClass} />
|
|
232
|
+
</div>
|
|
233
|
+
) : (
|
|
234
|
+
<div>
|
|
235
|
+
<label className={labelClass}>File Path</label>
|
|
236
|
+
<input type="text" value={zipLocalPath} onChange={(e) => setZipLocalPath(e.target.value)} placeholder="/home/user/docs.zip" className={inputClass} />
|
|
237
|
+
<p className={helpClass}>Absolute path on the server to a pre-downloaded ZIP file.</p>
|
|
238
|
+
</div>
|
|
239
|
+
)}
|
|
240
|
+
</div>
|
|
241
|
+
)}
|
|
242
|
+
|
|
243
|
+
{type === 'antora' && (
|
|
244
|
+
<div className="space-y-3">
|
|
245
|
+
<div>
|
|
246
|
+
<label className={labelClass}>Source</label>
|
|
247
|
+
<div className="flex gap-1.5">
|
|
248
|
+
<button type="button" onClick={() => setAntoraMode('git')} className={modeBtnClass(antoraMode === 'git')}>Git Repo</button>
|
|
249
|
+
<button type="button" onClick={() => setAntoraMode('localDir')} className={modeBtnClass(antoraMode === 'localDir')}>Local Dir</button>
|
|
250
|
+
<button type="button" onClick={() => setAntoraMode('zipFile')} className={modeBtnClass(antoraMode === 'zipFile')}>ZIP File</button>
|
|
251
|
+
</div>
|
|
252
|
+
</div>
|
|
253
|
+
{antoraMode === 'git' && (
|
|
254
|
+
<div>
|
|
255
|
+
<label className={labelClass}>Git Repository URL</label>
|
|
256
|
+
<input type="text" value={antoraRepoUrl} onChange={(e) => setAntoraRepoUrl(e.target.value)} placeholder="https://github.com/spring-projects/spring-boot.git" className={inputClass} />
|
|
257
|
+
</div>
|
|
258
|
+
)}
|
|
259
|
+
{antoraMode === 'localDir' && (
|
|
260
|
+
<div>
|
|
261
|
+
<label className={labelClass}>Directory Path</label>
|
|
262
|
+
<input type="text" value={antoraLocalPath} onChange={(e) => setAntoraLocalPath(e.target.value)} placeholder="/home/user/repos/spring-boot" className={inputClass} />
|
|
263
|
+
<p className={helpClass}>Absolute path to a pre-cloned Antora documentation repository.</p>
|
|
264
|
+
</div>
|
|
265
|
+
)}
|
|
266
|
+
{antoraMode === 'zipFile' && (
|
|
267
|
+
<div>
|
|
268
|
+
<label className={labelClass}>ZIP File Path</label>
|
|
269
|
+
<input type="text" value={antoraZipPath} onChange={(e) => setAntoraZipPath(e.target.value)} placeholder="/tmp/antora-content.zip" className={inputClass} />
|
|
270
|
+
</div>
|
|
271
|
+
)}
|
|
272
|
+
</div>
|
|
273
|
+
)}
|
|
274
|
+
|
|
275
|
+
{type === 'maven' && (
|
|
276
|
+
<div className="space-y-3">
|
|
277
|
+
<div>
|
|
278
|
+
<label className={labelClass}>Mode</label>
|
|
279
|
+
<div className="flex gap-1.5">
|
|
280
|
+
<button type="button" onClick={() => setMavenMode('direct')} className={modeBtnClass(mavenMode === 'direct')}>Direct Download</button>
|
|
281
|
+
<button type="button" onClick={() => setMavenMode('cli')} className={modeBtnClass(mavenMode === 'cli')}>Maven CLI</button>
|
|
282
|
+
<button type="button" onClick={() => setMavenMode('localJar')} className={modeBtnClass(mavenMode === 'localJar')}>Local JAR</button>
|
|
283
|
+
</div>
|
|
284
|
+
</div>
|
|
285
|
+
<div className="flex gap-3">
|
|
286
|
+
<div className="flex-1">
|
|
287
|
+
<label className={labelClass}>Group ID</label>
|
|
288
|
+
<input type="text" value={mavenGroupId} onChange={(e) => setMavenGroupId(e.target.value)} placeholder="io.quarkus" className={inputClass} />
|
|
289
|
+
</div>
|
|
290
|
+
<div className="flex-1">
|
|
291
|
+
<label className={labelClass}>Artifact ID</label>
|
|
292
|
+
<input type="text" value={mavenArtifactId} onChange={(e) => setMavenArtifactId(e.target.value)} placeholder="quarkus-core-docs" className={inputClass} />
|
|
293
|
+
</div>
|
|
294
|
+
</div>
|
|
295
|
+
<div className="flex gap-3">
|
|
296
|
+
<div className="flex-1">
|
|
297
|
+
<label className={labelClass}>Version</label>
|
|
298
|
+
<input type="text" value={mavenVersion} onChange={(e) => setMavenVersion(e.target.value)} placeholder="3.8.0" className={inputClass} />
|
|
299
|
+
</div>
|
|
300
|
+
<div className="flex-1">
|
|
301
|
+
<label className={labelClass}>Classifier</label>
|
|
302
|
+
<input type="text" value={mavenClassifier} onChange={(e) => setMavenClassifier(e.target.value)} placeholder="javadoc" className={inputClass} />
|
|
303
|
+
</div>
|
|
304
|
+
</div>
|
|
305
|
+
{mavenMode === 'cli' && (
|
|
306
|
+
<p className="text-xs text-accent bg-accent/5 ring-1 ring-accent/20 rounded-lg px-3 py-2">
|
|
307
|
+
Uses <code className="text-xs bg-accent/10 px-1 rounded">mvn dependency:copy</code> with your local
|
|
308
|
+
<code className="text-xs bg-accent/10 px-1 rounded ml-1">~/.m2/settings.xml</code> (proxy, mirrors, private repos).
|
|
309
|
+
Requires Maven installed and in PATH.
|
|
310
|
+
</p>
|
|
311
|
+
)}
|
|
312
|
+
{mavenMode === 'localJar' && (
|
|
313
|
+
<div>
|
|
314
|
+
<label className={labelClass}>JAR File Path</label>
|
|
315
|
+
<input type="text" value={mavenLocalJar} onChange={(e) => setMavenLocalJar(e.target.value)}
|
|
316
|
+
placeholder="/home/user/.m2/repository/.../library-1.0-javadoc.jar"
|
|
317
|
+
className={inputClass} />
|
|
318
|
+
<p className={helpClass}>Absolute path to a pre-downloaded javadoc JAR.</p>
|
|
319
|
+
</div>
|
|
320
|
+
)}
|
|
321
|
+
</div>
|
|
322
|
+
)}
|
|
323
|
+
|
|
324
|
+
{type === 'asciidoc' && (
|
|
325
|
+
<div className="space-y-3">
|
|
326
|
+
<div>
|
|
327
|
+
<label className={labelClass}>Source</label>
|
|
328
|
+
<div className="flex gap-1.5">
|
|
329
|
+
<button type="button" onClick={() => setAdocMode('git')} className={modeBtnClass(adocMode === 'git')}>Git Repo</button>
|
|
330
|
+
<button type="button" onClick={() => setAdocMode('localDir')} className={modeBtnClass(adocMode === 'localDir')}>Local Dir</button>
|
|
331
|
+
<button type="button" onClick={() => setAdocMode('zipFile')} className={modeBtnClass(adocMode === 'zipFile')}>ZIP File</button>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
{adocMode === 'git' && (
|
|
335
|
+
<div>
|
|
336
|
+
<label className={labelClass}>Repository URL</label>
|
|
337
|
+
<input type="text" value={adocRepoUrl} onChange={(e) => setAdocRepoUrl(e.target.value)} placeholder="https://github.com/quarkusio/quarkus.git" className={inputClass} />
|
|
338
|
+
</div>
|
|
339
|
+
)}
|
|
340
|
+
{adocMode === 'localDir' && (
|
|
341
|
+
<div>
|
|
342
|
+
<label className={labelClass}>Directory Path</label>
|
|
343
|
+
<input type="text" value={adocLocalPath} onChange={(e) => setAdocLocalPath(e.target.value)} placeholder="/home/user/repos/quarkus" className={inputClass} />
|
|
344
|
+
<p className={helpClass}>Absolute path to a pre-cloned repository with .adoc files.</p>
|
|
345
|
+
</div>
|
|
346
|
+
)}
|
|
347
|
+
{adocMode === 'zipFile' && (
|
|
348
|
+
<div>
|
|
349
|
+
<label className={labelClass}>ZIP File Path</label>
|
|
350
|
+
<input type="text" value={adocZipPath} onChange={(e) => setAdocZipPath(e.target.value)} placeholder="/tmp/docs.zip" className={inputClass} />
|
|
351
|
+
</div>
|
|
352
|
+
)}
|
|
353
|
+
<div>
|
|
354
|
+
<label className={labelClass}>Source Path <span className="text-text-muted font-normal">(optional)</span></label>
|
|
355
|
+
<input type="text" value={adocSourcePath} onChange={(e) => setAdocSourcePath(e.target.value)}
|
|
356
|
+
placeholder="e.g. docs/src/main/asciidoc" className={inputClass} />
|
|
357
|
+
<p className={helpClass}>Path within the source where .adoc files are located. Leave empty to scan entire source.</p>
|
|
358
|
+
</div>
|
|
359
|
+
</div>
|
|
360
|
+
)}
|
|
361
|
+
|
|
362
|
+
{type === 'github-markdown' && (
|
|
363
|
+
<div className="space-y-3">
|
|
364
|
+
<div>
|
|
365
|
+
<label className={labelClass}>Source</label>
|
|
366
|
+
<div className="flex gap-1.5">
|
|
367
|
+
<button type="button" onClick={() => setGhmdMode('git')} className={modeBtnClass(ghmdMode === 'git')}>Git Repo</button>
|
|
368
|
+
<button type="button" onClick={() => setGhmdMode('localDir')} className={modeBtnClass(ghmdMode === 'localDir')}>Local Dir</button>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
{ghmdMode === 'git' && (
|
|
372
|
+
<div>
|
|
373
|
+
<label className={labelClass}>Repository URL</label>
|
|
374
|
+
<input type="text" value={ghmdRepoUrl} onChange={(e) => setGhmdRepoUrl(e.target.value)} placeholder="https://github.com/reactjs/react.dev.git" className={inputClass} />
|
|
375
|
+
</div>
|
|
376
|
+
)}
|
|
377
|
+
{ghmdMode === 'localDir' && (
|
|
378
|
+
<div>
|
|
379
|
+
<label className={labelClass}>Directory Path</label>
|
|
380
|
+
<input type="text" value={ghmdLocalPath} onChange={(e) => setGhmdLocalPath(e.target.value)} placeholder="/home/user/repos/react.dev" className={inputClass} />
|
|
381
|
+
<p className={helpClass}>Absolute path to a pre-cloned repository with .md files.</p>
|
|
382
|
+
</div>
|
|
383
|
+
)}
|
|
384
|
+
<div>
|
|
385
|
+
<label className={labelClass}>Source Path <span className="text-text-muted font-normal">(optional)</span></label>
|
|
386
|
+
<input type="text" value={ghmdSourcePath} onChange={(e) => setGhmdSourcePath(e.target.value)}
|
|
387
|
+
placeholder="e.g. src/content" className={inputClass} />
|
|
388
|
+
<p className={helpClass}>Path within the source where .md files are located. Leave empty to scan entire source.</p>
|
|
389
|
+
</div>
|
|
390
|
+
{ghmdMode === 'git' && (
|
|
391
|
+
<div>
|
|
392
|
+
<label className={labelClass}>Branch <span className="text-text-muted font-normal">(optional)</span></label>
|
|
393
|
+
<input type="text" value={ghmdBranch} onChange={(e) => setGhmdBranch(e.target.value)}
|
|
394
|
+
placeholder="e.g. main" className={inputClass} />
|
|
395
|
+
<p className={helpClass}>Git branch to clone. Defaults to the repository default branch.</p>
|
|
396
|
+
</div>
|
|
397
|
+
)}
|
|
398
|
+
</div>
|
|
399
|
+
)}
|
|
400
|
+
|
|
401
|
+
{type === 'source-code' && (
|
|
402
|
+
<div className="space-y-3">
|
|
403
|
+
<div>
|
|
404
|
+
<label className={labelClass}>Source</label>
|
|
405
|
+
<div className="flex gap-1.5">
|
|
406
|
+
<button type="button" onClick={() => setScMode('git')} className={modeBtnClass(scMode === 'git')}>Git Repo</button>
|
|
407
|
+
<button type="button" onClick={() => setScMode('localDir')} className={modeBtnClass(scMode === 'localDir')}>Local Dir</button>
|
|
408
|
+
<button type="button" onClick={() => setScMode('zipFile')} className={modeBtnClass(scMode === 'zipFile')}>ZIP File</button>
|
|
409
|
+
</div>
|
|
410
|
+
</div>
|
|
411
|
+
{scMode === 'git' && (
|
|
412
|
+
<div>
|
|
413
|
+
<label className={labelClass}>Repository URL</label>
|
|
414
|
+
<input type="text" value={scRepoUrl} onChange={(e) => setScRepoUrl(e.target.value)} placeholder="https://github.com/org/repo.git" className={inputClass} />
|
|
415
|
+
</div>
|
|
416
|
+
)}
|
|
417
|
+
{scMode === 'localDir' && (
|
|
418
|
+
<div>
|
|
419
|
+
<label className={labelClass}>Directory Path</label>
|
|
420
|
+
<input type="text" value={scLocalPath} onChange={(e) => setScLocalPath(e.target.value)} placeholder="/home/user/repos/project" className={inputClass} />
|
|
421
|
+
<p className={helpClass}>Absolute path to a pre-cloned repository with source code.</p>
|
|
422
|
+
</div>
|
|
423
|
+
)}
|
|
424
|
+
{scMode === 'zipFile' && (
|
|
425
|
+
<div>
|
|
426
|
+
<label className={labelClass}>ZIP File Path</label>
|
|
427
|
+
<input type="text" value={scZipPath} onChange={(e) => setScZipPath(e.target.value)} placeholder="/tmp/source-code.zip" className={inputClass} />
|
|
428
|
+
</div>
|
|
429
|
+
)}
|
|
430
|
+
<div>
|
|
431
|
+
<label className={labelClass}>Source Path <span className="text-text-muted font-normal">(optional)</span></label>
|
|
432
|
+
<input type="text" value={scSourcePath} onChange={(e) => setScSourcePath(e.target.value)}
|
|
433
|
+
placeholder="e.g. src/main/java" className={inputClass} />
|
|
434
|
+
<p className={helpClass}>Directory within the source to scan. Leave empty to scan the entire source.</p>
|
|
435
|
+
</div>
|
|
436
|
+
{scMode === 'git' && (
|
|
437
|
+
<div>
|
|
438
|
+
<label className={labelClass}>Branch <span className="text-text-muted font-normal">(optional)</span></label>
|
|
439
|
+
<input type="text" value={scBranch} onChange={(e) => setScBranch(e.target.value)}
|
|
440
|
+
placeholder="e.g. main" className={inputClass} />
|
|
441
|
+
<p className={helpClass}>Git branch to clone. Defaults to the repository default branch.</p>
|
|
442
|
+
</div>
|
|
443
|
+
)}
|
|
444
|
+
<div className="px-3 py-2 bg-accent/5 ring-1 ring-accent/20 rounded-lg text-xs text-accent">
|
|
445
|
+
Generates a knowledge graph using Graphify (Tree-sitter AST analysis). Supports 15+ languages.
|
|
446
|
+
</div>
|
|
447
|
+
</div>
|
|
448
|
+
)}
|
|
449
|
+
|
|
450
|
+
{(type === 'asciidoc' || type === 'github-markdown' || type === 'antora') && (
|
|
451
|
+
<div className="space-y-2">
|
|
452
|
+
<div className="flex items-center gap-3">
|
|
453
|
+
<label className="relative inline-flex items-center cursor-pointer">
|
|
454
|
+
<input type="checkbox" className="sr-only peer" checked={graphifyEnabled} onChange={(e) => setGraphifyEnabled(e.target.checked)} />
|
|
455
|
+
<div className="w-9 h-5 bg-bg-alt ring-1 ring-border rounded-full peer peer-checked:bg-primary peer-checked:ring-primary/30 after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:rounded-full after:h-4 after:w-4 after:transition-all peer-checked:after:translate-x-full" />
|
|
456
|
+
</label>
|
|
457
|
+
<span className="text-sm font-medium text-text">Generate source code knowledge graph</span>
|
|
458
|
+
</div>
|
|
459
|
+
{graphifyEnabled && (
|
|
460
|
+
<div className="space-y-3">
|
|
461
|
+
<div>
|
|
462
|
+
<label className={labelClass}>Source Code Path <span className="text-text-muted font-normal">(optional)</span></label>
|
|
463
|
+
<input type="text" value={graphifySourcePath} onChange={(e) => setGraphifySourcePath(e.target.value)}
|
|
464
|
+
placeholder="e.g. core/src/main/java" className={inputClass} />
|
|
465
|
+
<p className={helpClass}>
|
|
466
|
+
Directory within the repo containing source code. Scan the entire repo if empty (may be slow for large repos).
|
|
467
|
+
</p>
|
|
468
|
+
</div>
|
|
469
|
+
<div className="px-3 py-2 bg-accent/5 ring-1 ring-accent/20 rounded-lg text-xs text-accent">
|
|
470
|
+
Runs Graphify (Tree-sitter AST) on the source code to build a structural dependency graph. Auto-installed via pip if not present.
|
|
471
|
+
</div>
|
|
472
|
+
</div>
|
|
473
|
+
)}
|
|
474
|
+
</div>
|
|
475
|
+
)}
|
|
476
|
+
|
|
477
|
+
{error && (
|
|
478
|
+
<div className="px-3 py-2 bg-danger/5 ring-1 ring-danger/20 rounded-lg text-sm text-danger">
|
|
479
|
+
{error}
|
|
480
|
+
</div>
|
|
481
|
+
)}
|
|
482
|
+
|
|
483
|
+
<div className="flex gap-2 justify-end pt-2">
|
|
484
|
+
<button type="button" onClick={onClose}
|
|
485
|
+
className="px-4 py-2 rounded-lg text-sm font-medium text-text-dim hover:bg-bg-alt hover:text-text transition-colors">
|
|
486
|
+
Cancel
|
|
487
|
+
</button>
|
|
488
|
+
<button type="submit" disabled={saving}
|
|
489
|
+
className="px-5 py-2 rounded-lg text-sm font-medium bg-primary text-white hover:bg-primary-hover transition-colors disabled:opacity-50">
|
|
490
|
+
{saving ? 'Saving...' : initial ? 'Save' : 'Add Source'}
|
|
491
|
+
</button>
|
|
492
|
+
</div>
|
|
493
|
+
</form>
|
|
494
|
+
</div>
|
|
495
|
+
</div>
|
|
496
|
+
);
|
|
497
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
export function useTheme() {
|
|
4
|
+
const [theme, setThemeState] = useState<'light' | 'dark'>(() => {
|
|
5
|
+
const stored = localStorage.getItem('dockit-theme');
|
|
6
|
+
if (stored === 'dark' || stored === 'light') return stored;
|
|
7
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
document.documentElement.classList.toggle('dark', theme === 'dark');
|
|
12
|
+
localStorage.setItem('dockit-theme', theme);
|
|
13
|
+
}, [theme]);
|
|
14
|
+
|
|
15
|
+
const setTheme = useCallback((t: 'light' | 'dark') => setThemeState(t), []);
|
|
16
|
+
const toggleTheme = useCallback(() => setThemeState((prev) => (prev === 'dark' ? 'light' : 'dark')), []);
|
|
17
|
+
|
|
18
|
+
return { theme, setTheme, toggleTheme };
|
|
19
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
@import "tailwindcss";
|
|
2
|
+
|
|
3
|
+
@theme {
|
|
4
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
|
5
|
+
--font-mono: "SF Mono", "Fira Code", "Fira Mono", "Roboto Mono", monospace;
|
|
6
|
+
|
|
7
|
+
--color-bg: #FAFAF9;
|
|
8
|
+
--color-bg-alt: #F5F5F4;
|
|
9
|
+
--color-surface: #FFFFFF;
|
|
10
|
+
--color-border: #E7E5E4;
|
|
11
|
+
--color-text: #1C1917;
|
|
12
|
+
--color-text-dim: #78716C;
|
|
13
|
+
--color-text-muted: #A8A29E;
|
|
14
|
+
--color-primary: #B45309;
|
|
15
|
+
--color-primary-hover: #92400E;
|
|
16
|
+
--color-primary-light: #FFFBEB;
|
|
17
|
+
--color-accent: #0F766E;
|
|
18
|
+
--color-danger: #DC2626;
|
|
19
|
+
--color-success: #16A34A;
|
|
20
|
+
--color-warning: #D97706;
|
|
21
|
+
--color-terminal-bg: #171717;
|
|
22
|
+
--color-terminal-fg: #A3E635;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
:root.dark {
|
|
26
|
+
color-scheme: dark;
|
|
27
|
+
--color-bg: #0C0A09;
|
|
28
|
+
--color-bg-alt: #171412;
|
|
29
|
+
--color-surface: #1C1917;
|
|
30
|
+
--color-border: #292524;
|
|
31
|
+
--color-text: #FAFAF9;
|
|
32
|
+
--color-text-dim: #A8A29E;
|
|
33
|
+
--color-text-muted: #78716C;
|
|
34
|
+
--color-primary: #F59E0B;
|
|
35
|
+
--color-primary-hover: #D97706;
|
|
36
|
+
--color-primary-light: #451A03;
|
|
37
|
+
--color-accent: #14B8A6;
|
|
38
|
+
--color-danger: #EF4444;
|
|
39
|
+
--color-success: #22C55E;
|
|
40
|
+
--color-warning: #F59E0B;
|
|
41
|
+
--color-terminal-bg: #0A0A0A;
|
|
42
|
+
--color-terminal-fg: #A3E635;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
* {
|
|
46
|
+
transition-property: color, background-color, border-color;
|
|
47
|
+
transition-duration: 150ms;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
body {
|
|
51
|
+
margin: 0;
|
|
52
|
+
min-height: 100vh;
|
|
53
|
+
background-color: var(--color-bg);
|
|
54
|
+
color: var(--color-text);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#root {
|
|
58
|
+
min-height: 100vh;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
::-webkit-scrollbar {
|
|
62
|
+
width: 6px;
|
|
63
|
+
height: 6px;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
::-webkit-scrollbar-track {
|
|
67
|
+
background: transparent;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
::-webkit-scrollbar-thumb {
|
|
71
|
+
background: var(--color-border);
|
|
72
|
+
border-radius: 3px;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
::-webkit-scrollbar-thumb:hover {
|
|
76
|
+
background: var(--color-text-muted);
|
|
77
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom/client';
|
|
3
|
+
import { BrowserRouter } from 'react-router-dom';
|
|
4
|
+
import App from './App';
|
|
5
|
+
import './index.css';
|
|
6
|
+
|
|
7
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
8
|
+
<React.StrictMode>
|
|
9
|
+
<BrowserRouter>
|
|
10
|
+
<App />
|
|
11
|
+
</BrowserRouter>
|
|
12
|
+
</React.StrictMode>
|
|
13
|
+
);
|