@lon-ask/dockit 0.1.1 → 0.1.2
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 +482 -337
- package/SKILL.md +94 -103
- 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 +261 -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/bin/commands/dev.ts +2 -2
- package/bin/commands/serve.ts +2 -2
- package/package.json +17 -3
package/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dockit
|
|
3
|
-
description: Documentation index and search tool
|
|
3
|
+
description: Documentation index and search tool providing on-demand framework/library docs and source code knowledge graphs for LLM context
|
|
4
4
|
license: MIT
|
|
5
5
|
compatibility: opencode
|
|
6
6
|
metadata:
|
|
@@ -8,147 +8,138 @@ metadata:
|
|
|
8
8
|
workflow: documentation
|
|
9
9
|
---
|
|
10
10
|
|
|
11
|
-
## What
|
|
12
|
-
- Search and retrieve documentation for frameworks and libraries (e.g., Quarkus, Spring Boot, React)
|
|
13
|
-
- Provide API documentation, class references, and configuration guides
|
|
14
|
-
- Fetch full document content for LLM context via CLI or MCP tools
|
|
11
|
+
## What dockit does
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
Use this skill when the user asks about:
|
|
18
|
-
- How to use a specific framework or library
|
|
19
|
-
- API documentation, class references, or configuration reference
|
|
20
|
-
- Any technology listed in available dockit entries
|
|
13
|
+
Dockit is a local documentation hub. It indexes documentation from multiple sources (GitHub Markdown, AsciiDoc, Antora, Maven Javadoc, ZIP archives) and builds source code knowledge graphs (Tree-sitter AST via Graphify). It provides hybrid TF-IDF + vector semantic search across all indexed content.
|
|
21
14
|
|
|
22
|
-
|
|
15
|
+
Run it from the terminal — no server process required. No internet needed after build.
|
|
23
16
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
### `dockit list`
|
|
27
|
-
Lists all configured documentation entries. Run this first to discover what's available.
|
|
28
|
-
|
|
29
|
-
### `dockit search [<entry>] <query>`
|
|
30
|
-
Searches documentation. Always provide the entry name as the first argument when you know which framework the question is about.
|
|
17
|
+
## Installation
|
|
31
18
|
|
|
32
19
|
```bash
|
|
33
|
-
|
|
34
|
-
dockit search react "how to create a hook"
|
|
35
|
-
dockit search quarkus "configure cache"
|
|
36
|
-
|
|
37
|
-
# Global search — top result per entry (when unsure which entry)
|
|
38
|
-
dockit search "cache"
|
|
20
|
+
npm install -g @lon-ask/dockit
|
|
39
21
|
```
|
|
40
22
|
|
|
41
|
-
|
|
42
|
-
Searches and fetches full document content for the top N results (default 3). This is the **primary command for LLMs** — it combines search + content retrieval in one step.
|
|
23
|
+
Or use `npx` without installing:
|
|
43
24
|
|
|
44
25
|
```bash
|
|
45
|
-
|
|
46
|
-
dockit search react "useState" --get-top
|
|
47
|
-
|
|
48
|
-
# Get full content for top 5 results, as JSON
|
|
49
|
-
dockit search react "hooks" --get-top 5 --json
|
|
26
|
+
npx @lon-ask/dockit <command>
|
|
50
27
|
```
|
|
51
28
|
|
|
52
|
-
|
|
53
|
-
|
|
29
|
+
All data is stored in `~/.dockit/` by default. Override with `DOCKIT_DATA_DIR`.
|
|
30
|
+
|
|
31
|
+
## When to use dockit
|
|
32
|
+
|
|
33
|
+
Use when you need:
|
|
34
|
+
- Up-to-date framework/library documentation instead of stale training data
|
|
35
|
+
- API reference, class docs, or configuration guides
|
|
36
|
+
- Source code structure analysis (imports, calls, inheritance graphs)
|
|
37
|
+
- To find which files/modules a function touches in a codebase
|
|
54
38
|
|
|
55
|
-
|
|
56
|
-
Builds documentation or checks build status.
|
|
39
|
+
## Core Workflow
|
|
57
40
|
|
|
58
|
-
|
|
41
|
+
### Step 1: Discover available documentation
|
|
59
42
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
-
|
|
43
|
+
```bash
|
|
44
|
+
dockit list
|
|
45
|
+
# or
|
|
46
|
+
npx @lon-ask/dockit list
|
|
47
|
+
```
|
|
64
48
|
|
|
65
|
-
|
|
49
|
+
### Step 2: Global search (find the right entry)
|
|
66
50
|
|
|
67
|
-
### Step 2: Search pattern
|
|
68
|
-
**Global search** (no entry) — returns top result per entry:
|
|
69
51
|
```bash
|
|
70
52
|
dockit search "cache"
|
|
53
|
+
# Returns top result per built entry
|
|
71
54
|
```
|
|
72
55
|
|
|
73
|
-
|
|
56
|
+
### Step 3: Scoped search with full content
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
dockit search quarkus "configure cache" --get-top 3
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The `--get-top` flag fetches full document text for the top N results. This is the primary command for LLMs — it combines search + retrieval in one invocation.
|
|
63
|
+
|
|
64
|
+
### Step 4: Knowledge graph queries (source-code entries)
|
|
65
|
+
|
|
66
|
+
For entries with `source-code` sources:
|
|
67
|
+
|
|
74
68
|
```bash
|
|
75
|
-
dockit
|
|
69
|
+
dockit graph query my-project "database" --limit 10 # find nodes by name/file/type
|
|
70
|
+
dockit graph gods my-project # most-connected nodes
|
|
71
|
+
dockit graph path my-project "app.ts" "database.ts" # dependency path
|
|
72
|
+
dockit graph explain my-project "createApp" # node details + connections
|
|
76
73
|
```
|
|
77
74
|
|
|
78
|
-
|
|
75
|
+
## CLI Reference
|
|
76
|
+
|
|
77
|
+
| Command | Purpose |
|
|
78
|
+
|---------|---------|
|
|
79
|
+
| `dockit list` | List all configured entries |
|
|
80
|
+
| `dockit search [<entry>] <query>` | Search docs (scoped or global) |
|
|
81
|
+
| `dockit search [<entry>] <query> --get-top [N]` | Search + full content for top N |
|
|
82
|
+
| `dockit get <entry> <path>` | Fetch specific document by path |
|
|
83
|
+
| `dockit build <entry>` | Build/rebuild documentation |
|
|
84
|
+
| `dockit status <entry>` | Check build status |
|
|
85
|
+
| `dockit init --path <dir> [--code-path <sub>]` | Index a local project |
|
|
86
|
+
| `dockit graph query <entry> <query>` | Search graph nodes |
|
|
87
|
+
| `dockit graph path <entry> <from> <to>` | Dependency path between nodes |
|
|
88
|
+
| `dockit graph gods <entry>` | Highest-degree (most connected) nodes |
|
|
89
|
+
| `dockit graph explain <entry> <node>` | Node details with edges |
|
|
90
|
+
|
|
91
|
+
## Query Refinement
|
|
79
92
|
|
|
80
|
-
|
|
81
|
-
Strip conversational filler. Keep only technical terms:
|
|
93
|
+
Strip conversational filler. Keep only technical keywords:
|
|
82
94
|
|
|
83
|
-
| User
|
|
95
|
+
| User question | Good query |
|
|
84
96
|
|---------------|------------|
|
|
85
97
|
| "How do I create a custom hook in React?" | `"create custom hook"` |
|
|
86
|
-
| "What
|
|
98
|
+
| "What's the Quarkus caching configuration?" | `"caching configuration"` |
|
|
99
|
+
| "How does the auth middleware work?" | `"auth middleware"` |
|
|
100
|
+
| "Find all files that import database.ts" | `graph query my-project "database.ts"` |
|
|
101
|
+
|
|
102
|
+
## Entry Types and Behavior
|
|
103
|
+
|
|
104
|
+
| Entry has | `dockit search` | `dockit graph` |
|
|
105
|
+
|-----------|----------------|-----------------|
|
|
106
|
+
| Docs only | Returns results | No graph available |
|
|
107
|
+
| Source code only | Returns empty | Use graph tools |
|
|
108
|
+
| Docs + code | Returns results (graph-boosted) | Graph tools work |
|
|
109
|
+
|
|
110
|
+
## Build Status
|
|
111
|
+
|
|
112
|
+
Entries start as `pending`. Build them before searching:
|
|
87
113
|
|
|
88
|
-
### Step 4: Handle missing builds
|
|
89
|
-
If an entry shows status `pending` or `error`, build it first:
|
|
90
114
|
```bash
|
|
91
|
-
dockit build
|
|
92
|
-
dockit status
|
|
115
|
+
dockit build quarkus
|
|
116
|
+
dockit status quarkus # wait for "ready"
|
|
93
117
|
```
|
|
94
118
|
|
|
95
|
-
|
|
119
|
+
If an entry shows `error`, check the build log via `dockit status <entry> --json`.
|
|
120
|
+
|
|
121
|
+
## MCP Tools
|
|
96
122
|
|
|
97
|
-
If
|
|
123
|
+
If configured as an MCP server, use these tools instead of CLI:
|
|
98
124
|
|
|
99
125
|
| MCP Tool | CLI Equivalent |
|
|
100
126
|
|----------|----------------|
|
|
101
127
|
| `dockit_list_entries` | `dockit list` |
|
|
128
|
+
| `dockit_find_entry` | — |
|
|
102
129
|
| `dockit_global_search` | `dockit search "query"` |
|
|
103
130
|
| `dockit_search` | `dockit search <entry> "query"` |
|
|
104
131
|
| `dockit_get_doc` | `dockit get <entry> <path>` |
|
|
105
|
-
| `dockit_build`
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
|
|
111
|
-
## Source Code Entries (Knowledge Graph)
|
|
112
|
-
|
|
113
|
-
For entries with `source-code` sources, the primary query mechanism is the **knowledge graph** instead of text search. Graphify's Tree-sitter AST pass parses 15+ languages and produces structural edges (*calls*, *imports*, *inherits*).
|
|
114
|
-
|
|
115
|
-
### Graph MCP Tools
|
|
116
|
-
|
|
117
|
-
| Tool | Description |
|
|
118
|
-
|------|-------------|
|
|
119
|
-
| `dockit_graph_query <entry> <query>` | Search graph nodes by name, file, or type |
|
|
120
|
-
| `dockit_graph_path <entry> <from> <to>` | Find shortest dependency path between two nodes |
|
|
121
|
-
| `dockit_graph_explain <entry> <node>` | Get node details with edges and connections |
|
|
122
|
-
| `dockit_graph_gods <entry>` | List most connected (highest-degree) nodes |
|
|
123
|
-
|
|
124
|
-
### Behavior by Entry Type
|
|
132
|
+
| `dockit_build` | `dockit build <entry>` |
|
|
133
|
+
| `dockit_build_status` | `dockit status <entry>` |
|
|
134
|
+
| `dockit_graph_query` | `dockit graph query` |
|
|
135
|
+
| `dockit_graph_path` | `dockit graph path` |
|
|
136
|
+
| `dockit_graph_explain` | `dockit graph explain` |
|
|
137
|
+
| `dockit_graph_gods` | `dockit graph gods` |
|
|
125
138
|
|
|
126
|
-
|
|
127
|
-
|------------|--------|-------------|
|
|
128
|
-
| Source-code only | `dockit search` returns empty | Use `dockit_graph_*` tools |
|
|
129
|
-
| Mixed (docs + code) | `dockit search` works + results graph-boosted | `dockit_graph_*` tools work |
|
|
130
|
-
| Docs only | `dockit search` works | No graph tools |
|
|
139
|
+
## Key Constraints
|
|
131
140
|
|
|
132
|
-
## Notes
|
|
133
141
|
- Documentation is plain text extracted from HTML
|
|
134
|
-
- Content is truncated at
|
|
135
|
-
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
After answering with documentation content, always display the source in a table at the end:
|
|
140
|
-
|
|
141
|
-
| Field | Value |
|
|
142
|
-
|-------|-------|
|
|
143
|
-
| **Type** | `<source type>` |
|
|
144
|
-
| **Label** | `<source label>` |
|
|
145
|
-
| **Repo** | `<repoUrl>` |
|
|
146
|
-
| **Source Path** | `<sourcePath>` |
|
|
147
|
-
| **Version** | `<entry version>` |
|
|
148
|
-
|
|
149
|
-
To get source details, use `--json` flag with search or check `dockit list --json`. Source fields come from the entry's `sources` array in `dockit.yaml`:
|
|
150
|
-
- `type` — source type (e.g., `github-markdown`, `asciidoc`, `maven`, `source-code`)
|
|
151
|
-
- `label` — human-readable label
|
|
152
|
-
- `repoUrl` or `localPath` — repository URL or local path
|
|
153
|
-
- `sourcePath` — path within the repo
|
|
154
|
-
- Entry `version` — the version of the documentation entry
|
|
142
|
+
- Content is truncated at 50 KB per document
|
|
143
|
+
- Documents must be built before searchable (status = `ready`)
|
|
144
|
+
- Knowledge graph requires `graphify` Python package and source-code source type
|
|
145
|
+
- All data is local. No cloud, no API keys required
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Dockit — Documentation Hub</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body class="antialiased">
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dockit/client",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc -b && vite build",
|
|
9
|
+
"preview": "vite preview"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"lucide-react": "^0.469.0",
|
|
13
|
+
"react": "^19.0.0",
|
|
14
|
+
"react-dom": "^19.0.0",
|
|
15
|
+
"react-router-dom": "^7.1.1"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@tailwindcss/vite": "^4.0.0",
|
|
19
|
+
"@types/react": "^19.0.3",
|
|
20
|
+
"@types/react-dom": "^19.0.2",
|
|
21
|
+
"@vitejs/plugin-react": "^4.3.4",
|
|
22
|
+
"tailwindcss": "^4.0.0",
|
|
23
|
+
"typescript": "^5.7.3",
|
|
24
|
+
"vite": "^6.0.7"
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Routes, Route } from 'react-router-dom';
|
|
2
|
+
import Layout from './components/Layout';
|
|
3
|
+
import EntryList from './components/EntryList';
|
|
4
|
+
import EntryForm from './components/EntryForm';
|
|
5
|
+
import EntryDetail from './components/EntryDetail';
|
|
6
|
+
|
|
7
|
+
export default function App() {
|
|
8
|
+
return (
|
|
9
|
+
<Layout>
|
|
10
|
+
<Routes>
|
|
11
|
+
<Route path="/" element={<EntryList />} />
|
|
12
|
+
<Route path="/entries/new" element={<EntryForm />} />
|
|
13
|
+
<Route path="/entries/:id/edit" element={<EntryForm />} />
|
|
14
|
+
<Route path="/entries/:id" element={<EntryDetail />} />
|
|
15
|
+
</Routes>
|
|
16
|
+
</Layout>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { Entry, EntryDetail, Source, SourceConfig, BuildStatusResponse, SearchResult } from '../types';
|
|
2
|
+
|
|
3
|
+
const BASE = '/api';
|
|
4
|
+
|
|
5
|
+
async function request<T>(url: string, options?: RequestInit): Promise<T> {
|
|
6
|
+
const res = await fetch(`${BASE}${url}`, {
|
|
7
|
+
headers: { 'Content-Type': 'application/json' },
|
|
8
|
+
...options,
|
|
9
|
+
});
|
|
10
|
+
if (!res.ok) {
|
|
11
|
+
const body = await res.json().catch(() => ({ error: res.statusText }));
|
|
12
|
+
throw new Error(body.error || `Request failed: ${res.status}`);
|
|
13
|
+
}
|
|
14
|
+
return res.json();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const api = {
|
|
18
|
+
entries: {
|
|
19
|
+
list: () => request<Entry[]>('/entries'),
|
|
20
|
+
get: (id: string) => request<EntryDetail>(`/entries/${id}`),
|
|
21
|
+
create: (data: { name: string; version: string; description?: string }) =>
|
|
22
|
+
request<Entry>('/entries', { method: 'POST', body: JSON.stringify(data) }),
|
|
23
|
+
update: (id: string, data: { name?: string; version?: string; description?: string }) =>
|
|
24
|
+
request<Entry>(`/entries/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
|
|
25
|
+
delete: (id: string) =>
|
|
26
|
+
request<{ success: boolean }>(`/entries/${id}`, { method: 'DELETE' }),
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
sources: {
|
|
30
|
+
create: (entryId: string, data: { type: string; label: string; config: SourceConfig }) =>
|
|
31
|
+
request<Source>(`/entries/${entryId}/sources`, { method: 'POST', body: JSON.stringify(data) }),
|
|
32
|
+
update: (id: string, data: { label?: string; config?: SourceConfig }) =>
|
|
33
|
+
request<Source>(`/sources/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
|
|
34
|
+
delete: (id: string) =>
|
|
35
|
+
request<{ success: boolean }>(`/sources/${id}`, { method: 'DELETE' }),
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
build: {
|
|
39
|
+
trigger: (entryId: string) =>
|
|
40
|
+
request<{ buildId: string; status: string }>(`/entries/${entryId}/build`, { method: 'POST' }),
|
|
41
|
+
status: (entryId: string) =>
|
|
42
|
+
request<BuildStatusResponse>(`/entries/${entryId}/build-status`),
|
|
43
|
+
cliScript: (entryId: string) =>
|
|
44
|
+
fetch(`${BASE}/entries/${entryId}/cli-script`).then((r) => r.text()),
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
search: (entryId: string, q: string) =>
|
|
48
|
+
request<SearchResult[]>(`/entries/${entryId}/search?q=${encodeURIComponent(q)}`),
|
|
49
|
+
|
|
50
|
+
searchGlobal: (q: string) =>
|
|
51
|
+
request<Array<SearchResult & { entryId: string; entryName: string; entryVersion: string }>>(`/search?q=${encodeURIComponent(q)}`),
|
|
52
|
+
|
|
53
|
+
bundleUrl: (entryId: string) => `${BASE}/bundle/${entryId}/`,
|
|
54
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { useEffect, useState, useRef } from 'react';
|
|
2
|
+
import { Terminal, ChevronDown, ChevronRight, Loader2, CheckCircle, AlertCircle } from 'lucide-react';
|
|
3
|
+
import type { BuildStatusResponse } from '../types';
|
|
4
|
+
import { api } from '../api/client';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
entryId: string;
|
|
8
|
+
refreshKey: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function BuildPanel({ entryId, refreshKey }: Props) {
|
|
12
|
+
const [status, setStatus] = useState<BuildStatusResponse | null>(null);
|
|
13
|
+
const [open, setOpen] = useState(false);
|
|
14
|
+
const logEndRef = useRef<HTMLDivElement>(null);
|
|
15
|
+
const intervalRef = useRef<ReturnType<typeof setInterval> | undefined>(undefined);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const fetch = async () => {
|
|
19
|
+
try {
|
|
20
|
+
const data = await api.build.status(entryId);
|
|
21
|
+
setStatus(data);
|
|
22
|
+
if (data.status === 'building') {
|
|
23
|
+
setOpen(true);
|
|
24
|
+
}
|
|
25
|
+
if (data.status === 'ready' || data.status === 'error') {
|
|
26
|
+
if (intervalRef.current) {
|
|
27
|
+
clearInterval(intervalRef.current);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
} catch {
|
|
31
|
+
// ignored
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
fetch();
|
|
35
|
+
intervalRef.current = setInterval(fetch, 1500);
|
|
36
|
+
return () => { if (intervalRef.current) clearInterval(intervalRef.current); };
|
|
37
|
+
}, [entryId, refreshKey]);
|
|
38
|
+
|
|
39
|
+
if (!status || status.status === 'none') return null;
|
|
40
|
+
|
|
41
|
+
const accentColor = status.status === 'ready' ? 'bg-success' :
|
|
42
|
+
status.status === 'error' ? 'bg-danger' : 'bg-warning';
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<div className="bg-surface ring-1 ring-border rounded-lg overflow-hidden">
|
|
46
|
+
<button
|
|
47
|
+
onClick={() => setOpen(!open)}
|
|
48
|
+
className="w-full flex items-center gap-2 px-3 py-2 hover:bg-bg-alt transition-colors text-left"
|
|
49
|
+
>
|
|
50
|
+
{open ? <ChevronDown size={14} className="text-text-muted" /> : <ChevronRight size={14} className="text-text-muted" />}
|
|
51
|
+
<Terminal size={14} className="text-text-dim" />
|
|
52
|
+
<span className="text-xs font-medium text-text">Build Log</span>
|
|
53
|
+
<span className="flex items-center gap-1 ml-auto">
|
|
54
|
+
{status.status === 'building' && <Loader2 size={12} className="animate-spin text-warning" />}
|
|
55
|
+
{status.status === 'ready' && <CheckCircle size={12} className="text-success" />}
|
|
56
|
+
{status.status === 'error' && <AlertCircle size={12} className="text-danger" />}
|
|
57
|
+
<span className={`text-[11px] font-medium ${
|
|
58
|
+
status.status === 'ready' ? 'text-success' :
|
|
59
|
+
status.status === 'error' ? 'text-danger' :
|
|
60
|
+
'text-warning'
|
|
61
|
+
}`}>
|
|
62
|
+
{status.status.charAt(0).toUpperCase() + status.status.slice(1)}
|
|
63
|
+
</span>
|
|
64
|
+
</span>
|
|
65
|
+
</button>
|
|
66
|
+
{open && (
|
|
67
|
+
<div className="relative">
|
|
68
|
+
<div className={`absolute left-0 top-0 bottom-0 w-0.5 ${accentColor}`} />
|
|
69
|
+
<div className="bg-terminal-bg text-terminal-fg p-3 pl-4 max-h-48 overflow-auto font-mono text-[11px] leading-relaxed">
|
|
70
|
+
<pre className="whitespace-pre-wrap">{status.log || 'Waiting for build to start...'}</pre>
|
|
71
|
+
<div ref={logEndRef} />
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
</div>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { ExternalLink, Maximize2, Minimize2, FileWarning, FileText } from 'lucide-react';
|
|
3
|
+
import { api } from '../api/client';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
entryId: string;
|
|
7
|
+
selectedFile?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default function DocViewer({ entryId, selectedFile }: Props) {
|
|
11
|
+
const [expanded, setExpanded] = useState(false);
|
|
12
|
+
const [error, setError] = useState(false);
|
|
13
|
+
const url = selectedFile
|
|
14
|
+
? `${api.bundleUrl(entryId)}${selectedFile}`
|
|
15
|
+
: api.bundleUrl(entryId);
|
|
16
|
+
|
|
17
|
+
useEffect(() => { setError(false); }, [selectedFile]);
|
|
18
|
+
|
|
19
|
+
if (!selectedFile) {
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex flex-col items-center justify-center h-full gap-3 text-center">
|
|
22
|
+
<div className="w-14 h-14 rounded-2xl bg-bg-alt flex items-center justify-center">
|
|
23
|
+
<FileText size={26} className="text-text-muted" />
|
|
24
|
+
</div>
|
|
25
|
+
<div>
|
|
26
|
+
<p className="text-sm text-text-dim font-medium">No document selected</p>
|
|
27
|
+
<p className="text-xs text-text-muted mt-1">Search for a document and click a result to view it here.</p>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (error) {
|
|
34
|
+
return (
|
|
35
|
+
<div className="flex flex-col items-center justify-center h-full gap-3 text-center">
|
|
36
|
+
<div className="w-14 h-14 rounded-2xl bg-bg-alt flex items-center justify-center">
|
|
37
|
+
<FileWarning size={26} className="text-text-muted" />
|
|
38
|
+
</div>
|
|
39
|
+
<div>
|
|
40
|
+
<p className="text-sm text-text-dim font-medium">Could not load document</p>
|
|
41
|
+
<p className="text-xs text-text-muted mt-1">The documentation may not be built yet. Click “Build” to generate it.</p>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<div className={`flex flex-col h-full ${expanded ? 'fixed inset-0 z-40 bg-bg p-4' : ''}`}>
|
|
49
|
+
<div className="flex items-center gap-2 mb-2 shrink-0">
|
|
50
|
+
<span className="text-xs text-text-muted font-mono truncate flex-1">{selectedFile}</span>
|
|
51
|
+
<button
|
|
52
|
+
onClick={() => setExpanded(!expanded)}
|
|
53
|
+
className="p-1 rounded text-text-muted hover:text-text hover:bg-bg-alt transition-colors"
|
|
54
|
+
title={expanded ? 'Exit fullscreen' : 'Fullscreen'}
|
|
55
|
+
>
|
|
56
|
+
{expanded ? <Minimize2 size={14} /> : <Maximize2 size={14} />}
|
|
57
|
+
</button>
|
|
58
|
+
<a
|
|
59
|
+
href={url}
|
|
60
|
+
target="_blank"
|
|
61
|
+
rel="noopener noreferrer"
|
|
62
|
+
className="p-1 rounded text-text-muted hover:text-text hover:bg-bg-alt transition-colors"
|
|
63
|
+
title="Open in new tab"
|
|
64
|
+
>
|
|
65
|
+
<ExternalLink size={14} />
|
|
66
|
+
</a>
|
|
67
|
+
</div>
|
|
68
|
+
<iframe
|
|
69
|
+
src={url}
|
|
70
|
+
onError={() => setError(true)}
|
|
71
|
+
className="flex-1 w-full ring-1 ring-border rounded-lg bg-surface min-h-0"
|
|
72
|
+
title="Documentation Viewer"
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|