@karmaniverous/jeeves-server 3.0.0-0
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/.env.local +13 -0
- package/.env.local.template +13 -0
- package/.tsbuildinfo +1 -0
- package/CHANGELOG.md +450 -0
- package/about.md +82 -0
- package/client/README.md +73 -0
- package/client/eslint.config.js +23 -0
- package/client/index.html +14 -0
- package/client/package-lock.json +5181 -0
- package/client/package.json +60 -0
- package/client/public/vite.svg +1 -0
- package/client/src/App.tsx +22 -0
- package/client/src/components/AccountMenu.tsx +167 -0
- package/client/src/components/ActionDropdown.tsx +120 -0
- package/client/src/components/CodeEditor.tsx +143 -0
- package/client/src/components/CodeViewer.tsx +113 -0
- package/client/src/components/ConfirmDialog.tsx +32 -0
- package/client/src/components/DirectoryRow.tsx +62 -0
- package/client/src/components/DirectoryTable.tsx +42 -0
- package/client/src/components/DownloadDropdown.tsx +116 -0
- package/client/src/components/DriveList.tsx +54 -0
- package/client/src/components/EmbeddedDiagramPanzoom.ts +28 -0
- package/client/src/components/FileContentView.tsx +155 -0
- package/client/src/components/InlineSvgPanzoom.ts +60 -0
- package/client/src/components/LazyDiagram.ts +93 -0
- package/client/src/components/LinkDropdown.tsx +134 -0
- package/client/src/components/MarkdownView.tsx +115 -0
- package/client/src/components/MermaidViewer.tsx +21 -0
- package/client/src/components/PlantUmlViewer.tsx +21 -0
- package/client/src/components/SearchModal.tsx +424 -0
- package/client/src/components/SvgViewer.tsx +107 -0
- package/client/src/components/TabBar.tsx +96 -0
- package/client/src/components/layout/Header.tsx +270 -0
- package/client/src/components/panzoom.ts +203 -0
- package/client/src/components/renderableUtils.ts +15 -0
- package/client/src/components/runner/JobTable.tsx +153 -0
- package/client/src/components/runner/RunHistory.tsx +140 -0
- package/client/src/components/runner/StatsBar.tsx +43 -0
- package/client/src/components/runner/StatusPill.tsx +27 -0
- package/client/src/components/runner/jobTableUtils.ts +65 -0
- package/client/src/components/scrollUtils.ts +39 -0
- package/client/src/components/ui/alert-dialog.tsx +107 -0
- package/client/src/components/ui/button.tsx +40 -0
- package/client/src/components/ui/dropdown-menu.tsx +79 -0
- package/client/src/components/ui/input.tsx +26 -0
- package/client/src/components/useActionState.ts +43 -0
- package/client/src/hooks/useFileBrowser.ts +102 -0
- package/client/src/hooks/useFileData.ts +78 -0
- package/client/src/hooks/useScrollAnchor.ts +70 -0
- package/client/src/hooks/useShareSettings.ts +22 -0
- package/client/src/hooks/useTopBar.ts +27 -0
- package/client/src/index.css +281 -0
- package/client/src/lib/AuthContext.ts +27 -0
- package/client/src/lib/api.ts +239 -0
- package/client/src/lib/auth.tsx +50 -0
- package/client/src/lib/codeBlockCm6.ts +129 -0
- package/client/src/lib/codeBlockCopy.ts +43 -0
- package/client/src/lib/codemirror.ts +77 -0
- package/client/src/lib/runner-api.ts +172 -0
- package/client/src/lib/svg.ts +50 -0
- package/client/src/lib/theme.ts +34 -0
- package/client/src/lib/utils.ts +6 -0
- package/client/src/main.tsx +11 -0
- package/client/src/pages/FileBrowser.tsx +135 -0
- package/client/src/pages/Home.tsx +46 -0
- package/client/src/pages/Runner.tsx +151 -0
- package/client/src/pages/RunnerJob.tsx +170 -0
- package/client/tsconfig.app.json +32 -0
- package/client/tsconfig.json +7 -0
- package/client/tsconfig.node.json +26 -0
- package/client/vite.config.ts +35 -0
- package/content/privacy.md +61 -0
- package/content/terms.md +41 -0
- package/dist/client/assets/CodeEditor-0XHVI8Nu.js +1 -0
- package/dist/client/assets/CodeViewer-CykMVsfX.js +1 -0
- package/dist/client/assets/index--MBieNJA.js +1 -0
- package/dist/client/assets/index-BENeXQI_.js +1 -0
- package/dist/client/assets/index-BbBpoOxz.js +1 -0
- package/dist/client/assets/index-BdV9g5AM.js +6 -0
- package/dist/client/assets/index-BjAilRri.js +2 -0
- package/dist/client/assets/index-BqbhWo2I.js +3 -0
- package/dist/client/assets/index-CVbycZ0H.js +1 -0
- package/dist/client/assets/index-Cs5oz2oJ.js +5 -0
- package/dist/client/assets/index-D8KZVveX.js +1 -0
- package/dist/client/assets/index-DC4HMHxY.js +13 -0
- package/dist/client/assets/index-DbMebkkd.css +1 -0
- package/dist/client/assets/index-DcY2RXqX.js +1 -0
- package/dist/client/assets/index-Duy-tZYV.js +1 -0
- package/dist/client/assets/index-Dw7rDFmE.js +7 -0
- package/dist/client/assets/index-FlCUvrjv.js +2 -0
- package/dist/client/assets/index-K6OVmfhg.js +1 -0
- package/dist/client/assets/index-LjwgzZ7F.js +62 -0
- package/dist/client/assets/index-MLwyFRN0.js +1 -0
- package/dist/client/assets/index-OpqBpSjn.js +1 -0
- package/dist/client/assets/index-SsHei0HE.js +1 -0
- package/dist/client/assets/index-uQa2yckk.js +1 -0
- package/dist/client/assets/index-udkXoIER.js +1 -0
- package/dist/client/index.html +15 -0
- package/dist/client/vite.svg +1 -0
- package/dist/src/auth/google.js +57 -0
- package/dist/src/auth/keys.js +185 -0
- package/dist/src/auth/resolve.js +102 -0
- package/dist/src/auth/session.js +57 -0
- package/dist/src/cli/commands/config.js +100 -0
- package/dist/src/cli/commands/config.test.js +84 -0
- package/dist/src/cli/commands/service.js +93 -0
- package/dist/src/cli/commands/start.js +24 -0
- package/dist/src/cli/index.js +20 -0
- package/dist/src/config/index.js +90 -0
- package/dist/src/config/loadConfig.test.js +127 -0
- package/dist/src/config/resolve.js +134 -0
- package/dist/src/config/resolve.test.js +148 -0
- package/dist/src/config/schema.js +159 -0
- package/dist/src/config/substituteEnvVars.js +45 -0
- package/dist/src/config/substituteEnvVars.test.js +51 -0
- package/dist/src/config/types.js +5 -0
- package/dist/src/routes/api/auth-status.js +56 -0
- package/dist/src/routes/api/diagrams.js +35 -0
- package/dist/src/routes/api/directory.js +93 -0
- package/dist/src/routes/api/drives.js +15 -0
- package/dist/src/routes/api/export.js +218 -0
- package/dist/src/routes/api/fileContent.js +286 -0
- package/dist/src/routes/api/index.js +33 -0
- package/dist/src/routes/api/linkInfo.js +71 -0
- package/dist/src/routes/api/linkInfo.test.js +104 -0
- package/dist/src/routes/api/middleware.js +117 -0
- package/dist/src/routes/api/raw.js +38 -0
- package/dist/src/routes/api/runner.js +59 -0
- package/dist/src/routes/api/search.js +236 -0
- package/dist/src/routes/api/sharing.js +203 -0
- package/dist/src/routes/api/status.js +68 -0
- package/dist/src/routes/api/status.test.js +62 -0
- package/dist/src/routes/auth.js +99 -0
- package/dist/src/routes/event.js +77 -0
- package/dist/src/routes/event.test.js +206 -0
- package/dist/src/routes/health.js +10 -0
- package/dist/src/routes/keys.js +129 -0
- package/dist/src/routes/path/index.js +17 -0
- package/dist/src/routes/static.js +30 -0
- package/dist/src/server.js +90 -0
- package/dist/src/services/deepShareLinks.js +163 -0
- package/dist/src/services/diagramCache.js +104 -0
- package/dist/src/services/embeddedDiagrams.js +136 -0
- package/dist/src/services/eventLog.js +55 -0
- package/dist/src/services/eventLog.test.js +113 -0
- package/dist/src/services/eventQueue.js +154 -0
- package/dist/src/services/eventQueue.test.js +104 -0
- package/dist/src/services/export.js +220 -0
- package/dist/src/services/exportCache.js +196 -0
- package/dist/src/services/markdown.js +147 -0
- package/dist/src/services/mermaid.js +97 -0
- package/dist/src/services/plantuml.js +145 -0
- package/dist/src/services/puppeteer.js +156 -0
- package/dist/src/util/breadcrumbs.js +22 -0
- package/dist/src/util/crypto.js +56 -0
- package/dist/src/util/crypto.test.js +99 -0
- package/dist/src/util/fileDetection.js +66 -0
- package/dist/src/util/fileDetection.test.js +89 -0
- package/dist/src/util/formatters.js +43 -0
- package/dist/src/util/formatters.test.js +83 -0
- package/dist/src/util/packageVersion.js +25 -0
- package/dist/src/util/platform.js +148 -0
- package/dist/src/util/state.js +46 -0
- package/dist/vitest.config.js +12 -0
- package/favicon.svg +3 -0
- package/guides/access-decision-flow.mmd +24 -0
- package/guides/access-decision-flow.svg +1 -0
- package/guides/api-integration.md +236 -0
- package/guides/deployment.md +287 -0
- package/guides/event-gateway.md +204 -0
- package/guides/event-gateway.mmd +17 -0
- package/guides/event-gateway.svg +1 -0
- package/guides/exports.md +239 -0
- package/guides/setup.md +313 -0
- package/guides/sharing.md +204 -0
- package/jeeves-server.config.template.json +25 -0
- package/package.json +124 -0
- package/scripts/download-plantuml.js +70 -0
- package/src/auth/google.ts +93 -0
- package/src/auth/keys.ts +252 -0
- package/src/auth/resolve.ts +157 -0
- package/src/auth/session.ts +77 -0
- package/src/cli/commands/config.test.ts +107 -0
- package/src/cli/commands/config.ts +113 -0
- package/src/cli/commands/service.ts +129 -0
- package/src/cli/commands/start.ts +27 -0
- package/src/cli/index.ts +25 -0
- package/src/config/index.ts +113 -0
- package/src/config/loadConfig.test.ts +155 -0
- package/src/config/resolve.test.ts +192 -0
- package/src/config/resolve.ts +173 -0
- package/src/config/schema.ts +179 -0
- package/src/config/substituteEnvVars.test.ts +64 -0
- package/src/config/substituteEnvVars.ts +52 -0
- package/src/config/types.ts +129 -0
- package/src/routes/api/auth-status.ts +85 -0
- package/src/routes/api/diagrams.ts +53 -0
- package/src/routes/api/directory.ts +123 -0
- package/src/routes/api/drives.ts +23 -0
- package/src/routes/api/export.ts +314 -0
- package/src/routes/api/fileContent.ts +414 -0
- package/src/routes/api/index.ts +37 -0
- package/src/routes/api/linkInfo.test.ts +132 -0
- package/src/routes/api/linkInfo.ts +83 -0
- package/src/routes/api/middleware.ts +156 -0
- package/src/routes/api/raw.ts +54 -0
- package/src/routes/api/runner.ts +107 -0
- package/src/routes/api/search.ts +321 -0
- package/src/routes/api/sharing.ts +259 -0
- package/src/routes/api/status.test.ts +72 -0
- package/src/routes/api/status.ts +82 -0
- package/src/routes/auth.ts +143 -0
- package/src/routes/event.test.ts +248 -0
- package/src/routes/event.ts +109 -0
- package/src/routes/health.ts +13 -0
- package/src/routes/keys.ts +192 -0
- package/src/routes/path/index.ts +24 -0
- package/src/routes/static.ts +54 -0
- package/src/server.ts +104 -0
- package/src/services/deepShareLinks.ts +203 -0
- package/src/services/diagramCache.ts +128 -0
- package/src/services/embeddedDiagrams.ts +168 -0
- package/src/services/eventLog.test.ts +144 -0
- package/src/services/eventLog.ts +68 -0
- package/src/services/eventQueue.test.ts +127 -0
- package/src/services/eventQueue.ts +196 -0
- package/src/services/export.ts +267 -0
- package/src/services/exportCache.ts +216 -0
- package/src/services/markdown.ts +189 -0
- package/src/services/mermaid.ts +113 -0
- package/src/services/plantuml.ts +172 -0
- package/src/services/puppeteer.ts +188 -0
- package/src/types/fastify.d.ts +13 -0
- package/src/types/jsonmap.d.ts +10 -0
- package/src/types/plantuml-encoder.d.ts +4 -0
- package/src/util/breadcrumbs.ts +33 -0
- package/src/util/crypto.test.ts +132 -0
- package/src/util/crypto.ts +79 -0
- package/src/util/fileDetection.test.ts +115 -0
- package/src/util/fileDetection.ts +70 -0
- package/src/util/formatters.test.ts +105 -0
- package/src/util/formatters.ts +44 -0
- package/src/util/packageVersion.ts +30 -0
- package/src/util/platform.ts +178 -0
- package/src/util/state.ts +55 -0
- package/test-docs/diagram-retry-test.md +18 -0
- package/test-docs/embedded-diagrams.md +52 -0
- package/test-docs/lazy-diagrams-test.md +333 -0
- package/test-docs/page-a.md +7 -0
- package/test-docs/page-b.md +7 -0
- package/test-docs/page-c.md +7 -0
- package/test-docs/sub/page-d.md +7 -0
- package/test-docs/test-diagram.puml +13 -0
- package/test-docs/validate-deep-share.js +318 -0
- package/tsconfig.json +37 -0
- package/tsdoc.json +13 -0
- package/vendor/.plantuml-version +1 -0
- package/vendor/plantuml.jar +0 -0
- package/vitest.config.js +12 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SVG normalization utilities shared between components.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Normalize an SVG string for responsive display:
|
|
7
|
+
* - Fix preserveAspectRatio="none" (PlantUML quirk)
|
|
8
|
+
* - Remove inline width/height styles
|
|
9
|
+
* - Set width="100%" and let viewBox handle sizing
|
|
10
|
+
* - Ensure viewBox exists if dimensions are available
|
|
11
|
+
*/
|
|
12
|
+
export function normalizeSvg(svgText: string): string {
|
|
13
|
+
const parser = new DOMParser();
|
|
14
|
+
const doc = parser.parseFromString(svgText, 'image/svg+xml');
|
|
15
|
+
const svg = doc.querySelector('svg');
|
|
16
|
+
if (!svg) return svgText;
|
|
17
|
+
|
|
18
|
+
// Extract intrinsic dimensions for viewBox if missing
|
|
19
|
+
let w = 0, h = 0;
|
|
20
|
+
const viewBox = svg.getAttribute('viewBox');
|
|
21
|
+
if (viewBox) {
|
|
22
|
+
const parts = viewBox.split(/[\s,]+/).map(Number);
|
|
23
|
+
if (parts.length === 4) { w = parts[2]; h = parts[3]; }
|
|
24
|
+
}
|
|
25
|
+
if (w <= 0 || h <= 0) {
|
|
26
|
+
w = parseFloat(svg.getAttribute('width') ?? '0');
|
|
27
|
+
h = parseFloat(svg.getAttribute('height') ?? '0');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Ensure viewBox exists
|
|
31
|
+
if (!svg.getAttribute('viewBox') && w > 0 && h > 0) {
|
|
32
|
+
svg.setAttribute('viewBox', `0 0 ${w} ${h}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Fix PlantUML preserveAspectRatio="none"
|
|
36
|
+
if (svg.getAttribute('preserveAspectRatio') === 'none') {
|
|
37
|
+
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Strip fixed sizing
|
|
41
|
+
svg.removeAttribute('height');
|
|
42
|
+
svg.style.removeProperty('width');
|
|
43
|
+
svg.style.removeProperty('height');
|
|
44
|
+
svg.style.removeProperty('background');
|
|
45
|
+
|
|
46
|
+
// Set responsive sizing
|
|
47
|
+
svg.setAttribute('width', '100%');
|
|
48
|
+
|
|
49
|
+
return new XMLSerializer().serializeToString(doc);
|
|
50
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
type Theme = 'light' | 'dark';
|
|
4
|
+
|
|
5
|
+
function applyThemeClass(theme: Theme) {
|
|
6
|
+
if (theme === 'dark') {
|
|
7
|
+
document.documentElement.classList.add('dark');
|
|
8
|
+
} else {
|
|
9
|
+
document.documentElement.classList.remove('dark');
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function useTheme(): [Theme, () => void] {
|
|
14
|
+
const [theme, setTheme] = useState<Theme>(() => {
|
|
15
|
+
const saved = localStorage.getItem('jeeves-theme');
|
|
16
|
+
const t = (saved === 'dark' ? 'dark' : 'light') as Theme;
|
|
17
|
+
applyThemeClass(t);
|
|
18
|
+
return t;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
applyThemeClass(theme);
|
|
23
|
+
}, [theme]);
|
|
24
|
+
|
|
25
|
+
const toggleTheme = useCallback(() => {
|
|
26
|
+
setTheme((prev) => {
|
|
27
|
+
const next = prev === 'dark' ? 'light' : 'dark';
|
|
28
|
+
localStorage.setItem('jeeves-theme', next);
|
|
29
|
+
return next;
|
|
30
|
+
});
|
|
31
|
+
}, []);
|
|
32
|
+
|
|
33
|
+
return [theme, toggleTheme];
|
|
34
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FileBrowser — thin composition root that wires hooks and components together.
|
|
3
|
+
*/
|
|
4
|
+
import { ConfirmDialog } from '@/components/ConfirmDialog';
|
|
5
|
+
import { DirectoryTable } from '@/components/DirectoryTable';
|
|
6
|
+
import { DownloadDropdown } from '@/components/DownloadDropdown';
|
|
7
|
+
import { DriveList } from '@/components/DriveList';
|
|
8
|
+
import { FileContentView } from '@/components/FileContentView';
|
|
9
|
+
import { Header } from '@/components/layout/Header';
|
|
10
|
+
import { LinkDropdown } from '@/components/LinkDropdown';
|
|
11
|
+
import { TabBar } from '@/components/TabBar';
|
|
12
|
+
import { useFileBrowser } from '@/hooks/useFileBrowser';
|
|
13
|
+
|
|
14
|
+
export function FileBrowser() {
|
|
15
|
+
const {
|
|
16
|
+
reqPath, theme, toggleTheme,
|
|
17
|
+
shareSettings, setShareSettings,
|
|
18
|
+
mobileTocOpen, setMobileTocOpen,
|
|
19
|
+
proseWidth, toggleProseWidth,
|
|
20
|
+
drives, directory, fileRaw, fileRendered, file,
|
|
21
|
+
loading, error, editing, setEditing,
|
|
22
|
+
viewTab, setViewTab,
|
|
23
|
+
breadcrumbs, isInsider, searchEnabled, keyAge,
|
|
24
|
+
rotateKeyDialogOpen, setRotateKeyDialogOpen,
|
|
25
|
+
handleRotateKey, confirmRotateKey,
|
|
26
|
+
topBarRef, mainRef, topBarHeight,
|
|
27
|
+
handleSave,
|
|
28
|
+
} = useFileBrowser();
|
|
29
|
+
|
|
30
|
+
const showFileView = file || (loading && !!reqPath);
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="min-h-screen bg-background text-foreground">
|
|
34
|
+
{/* Fixed top bar */}
|
|
35
|
+
<div ref={topBarRef} className="fixed top-0 left-0 right-0 z-50">
|
|
36
|
+
<Header
|
|
37
|
+
breadcrumbs={breadcrumbs}
|
|
38
|
+
isInsider={isInsider}
|
|
39
|
+
searchEnabled={searchEnabled}
|
|
40
|
+
theme={theme}
|
|
41
|
+
onToggleTheme={toggleTheme}
|
|
42
|
+
keyAge={editing ? undefined : keyAge}
|
|
43
|
+
onRotateKey={editing ? undefined : handleRotateKey}
|
|
44
|
+
downloadDropdown={editing ? undefined :
|
|
45
|
+
file ? (
|
|
46
|
+
<DownloadDropdown reqPath={reqPath} file={file} isInsider={isInsider} variant="header" />
|
|
47
|
+
) : directory ? (
|
|
48
|
+
<DownloadDropdown reqPath={reqPath} file={null} isDirectory isInsider={isInsider} variant="header" />
|
|
49
|
+
) : undefined
|
|
50
|
+
}
|
|
51
|
+
downloadMenuItem={editing ? undefined :
|
|
52
|
+
file ? (
|
|
53
|
+
(onDismiss) => <DownloadDropdown reqPath={reqPath} file={file} isInsider={isInsider} variant="menuItem" onStateChange={(s) => { if (s === 'done') setTimeout(onDismiss, 800); }} />
|
|
54
|
+
) : directory ? (
|
|
55
|
+
(onDismiss) => <DownloadDropdown reqPath={reqPath} file={null} isDirectory isInsider={isInsider} variant="menuItem" onStateChange={(s) => { if (s === 'done') setTimeout(onDismiss, 800); }} />
|
|
56
|
+
) : undefined
|
|
57
|
+
}
|
|
58
|
+
linkControls={editing ? undefined : isInsider ? (
|
|
59
|
+
<LinkDropdown path={`/${reqPath}`} shareSettings={shareSettings} onShareSettingsChange={setShareSettings} showEvent showRaw={!!file} variant="header" isDirectory={!file} />
|
|
60
|
+
) : undefined}
|
|
61
|
+
linkMenuItem={editing ? undefined : isInsider ? (
|
|
62
|
+
(onDismiss) => <LinkDropdown path={`/${reqPath}`} shareSettings={shareSettings} onShareSettingsChange={setShareSettings} showEvent showRaw={!!file} variant="menuItem" isDirectory={!file} onStateChange={(s) => { if (s === 'done') setTimeout(onDismiss, 800); }} />
|
|
63
|
+
) : undefined}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
{/* Tabs for file views */}
|
|
67
|
+
{showFileView && (
|
|
68
|
+
<TabBar
|
|
69
|
+
reqPath={reqPath}
|
|
70
|
+
file={file}
|
|
71
|
+
fileRendered={fileRendered}
|
|
72
|
+
viewTab={viewTab}
|
|
73
|
+
setViewTab={setViewTab}
|
|
74
|
+
proseWidth={proseWidth}
|
|
75
|
+
toggleProseWidth={toggleProseWidth}
|
|
76
|
+
isInsider={isInsider}
|
|
77
|
+
editing={editing}
|
|
78
|
+
setEditing={setEditing}
|
|
79
|
+
mobileTocOpen={mobileTocOpen}
|
|
80
|
+
setMobileTocOpen={setMobileTocOpen}
|
|
81
|
+
loading={loading}
|
|
82
|
+
/>
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<main
|
|
87
|
+
ref={mainRef}
|
|
88
|
+
className={showFileView ? 'px-0 pb-32 overflow-y-auto' : 'p-4 pb-32 md:px-6 md:pt-6 overflow-y-auto'}
|
|
89
|
+
style={{ marginTop: `${topBarHeight}px`, height: `calc(100vh - ${topBarHeight}px)` }}
|
|
90
|
+
>
|
|
91
|
+
{loading && !reqPath && <div className="text-muted-foreground text-sm">Loading...</div>}
|
|
92
|
+
{error && <div className="text-destructive text-sm">Error: {error}</div>}
|
|
93
|
+
|
|
94
|
+
{/* Drive listing */}
|
|
95
|
+
{!loading && !error && drives && (
|
|
96
|
+
<DriveList drives={drives} isInsider={isInsider} shareSettings={shareSettings} onShareSettingsChange={setShareSettings} />
|
|
97
|
+
)}
|
|
98
|
+
|
|
99
|
+
{/* Directory listing */}
|
|
100
|
+
{!loading && !error && directory && (
|
|
101
|
+
<DirectoryTable entries={directory.entries} basePath={reqPath} isInsider={isInsider} shareSettings={shareSettings} onShareSettingsChange={setShareSettings} />
|
|
102
|
+
)}
|
|
103
|
+
|
|
104
|
+
{/* File viewer */}
|
|
105
|
+
{!error && showFileView && (
|
|
106
|
+
<FileContentView
|
|
107
|
+
reqPath={reqPath}
|
|
108
|
+
file={file}
|
|
109
|
+
fileRaw={fileRaw}
|
|
110
|
+
fileRendered={fileRendered}
|
|
111
|
+
viewTab={viewTab}
|
|
112
|
+
editing={editing}
|
|
113
|
+
setEditing={setEditing}
|
|
114
|
+
proseWidth={proseWidth}
|
|
115
|
+
topBarHeight={topBarHeight}
|
|
116
|
+
mainRef={mainRef}
|
|
117
|
+
mobileTocOpen={mobileTocOpen}
|
|
118
|
+
setMobileTocOpen={setMobileTocOpen}
|
|
119
|
+
onSave={handleSave}
|
|
120
|
+
loading={loading}
|
|
121
|
+
/>
|
|
122
|
+
)}
|
|
123
|
+
</main>
|
|
124
|
+
|
|
125
|
+
<ConfirmDialog
|
|
126
|
+
open={rotateKeyDialogOpen}
|
|
127
|
+
onOpenChange={setRotateKeyDialogOpen}
|
|
128
|
+
title="Rotate insider key?"
|
|
129
|
+
description="This will invalidate ALL existing share links generated with your current key. This action cannot be undone."
|
|
130
|
+
confirmLabel="Rotate Key"
|
|
131
|
+
onConfirm={() => void confirmRotateKey()}
|
|
132
|
+
/>
|
|
133
|
+
</div>
|
|
134
|
+
);
|
|
135
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useNavigate } from 'react-router-dom';
|
|
3
|
+
|
|
4
|
+
import { useAuth } from '@/lib/AuthContext';
|
|
5
|
+
|
|
6
|
+
export function Home() {
|
|
7
|
+
const { loading, authenticated } = useAuth();
|
|
8
|
+
const navigate = useNavigate();
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
if (!loading && authenticated) {
|
|
11
|
+
navigate('/browse', { replace: true });
|
|
12
|
+
}
|
|
13
|
+
}, [loading, authenticated, navigate]);
|
|
14
|
+
|
|
15
|
+
if (loading) {
|
|
16
|
+
return (
|
|
17
|
+
<div className="min-h-screen bg-background flex items-center justify-center">
|
|
18
|
+
<div className="text-muted-foreground text-sm">Loading...</div>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<div className="min-h-screen bg-background flex items-center justify-center">
|
|
25
|
+
<div className="text-center space-y-6">
|
|
26
|
+
<div className="text-6xl mb-2">🎩</div>
|
|
27
|
+
<h1 className="text-2xl font-semibold text-foreground">Jeeves</h1>
|
|
28
|
+
<p className="text-muted-foreground text-sm max-w-xs mx-auto">
|
|
29
|
+
File browser and document server
|
|
30
|
+
</p>
|
|
31
|
+
<a
|
|
32
|
+
href="/auth/login?returnTo=/browse"
|
|
33
|
+
className="inline-flex items-center gap-3 px-6 py-3 bg-white text-zinc-800 rounded-lg shadow-md hover:shadow-lg transition-shadow font-medium text-sm border border-zinc-200"
|
|
34
|
+
>
|
|
35
|
+
<svg viewBox="0 0 24 24" width="20" height="20" className="shrink-0">
|
|
36
|
+
<path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 0 1-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" />
|
|
37
|
+
<path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" />
|
|
38
|
+
<path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" />
|
|
39
|
+
<path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" />
|
|
40
|
+
</svg>
|
|
41
|
+
Sign in with Google
|
|
42
|
+
</a>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner dashboard — job list view with stats summary and auto-refresh.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { RefreshCw } from 'lucide-react';
|
|
6
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { Header } from '@/components/layout/Header';
|
|
9
|
+
import { JobTableBody, JobTableHeader, } from '@/components/runner/JobTable';
|
|
10
|
+
import { nextSort, sortJobs } from '@/components/runner/jobTableUtils';
|
|
11
|
+
import type { SortColumn, SortState } from '@/components/runner/JobTable';
|
|
12
|
+
import { StatsBar } from '@/components/runner/StatsBar';
|
|
13
|
+
import { Button } from '@/components/ui/button';
|
|
14
|
+
import { useAuth } from '@/lib/AuthContext';
|
|
15
|
+
import type { RunnerJob, RunnerStats } from '@/lib/runner-api';
|
|
16
|
+
import { getRunnerJobs, getRunnerStats, triggerJobRun } from '@/lib/runner-api';
|
|
17
|
+
import { useTheme } from '@/lib/theme';
|
|
18
|
+
|
|
19
|
+
const REFRESH_INTERVAL = 10_000;
|
|
20
|
+
|
|
21
|
+
export function Runner() {
|
|
22
|
+
const [jobs, setJobs] = useState<RunnerJob[]>([]);
|
|
23
|
+
const [stats, setStats] = useState<RunnerStats | null>(null);
|
|
24
|
+
const [error, setError] = useState<string | null>(null);
|
|
25
|
+
const [loading, setLoading] = useState(true);
|
|
26
|
+
const [autoRefresh, setAutoRefresh] = useState(true);
|
|
27
|
+
const [sort, setSort] = useState<SortState>({ column: null, direction: 'desc' });
|
|
28
|
+
const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
29
|
+
const [theme, toggleTheme] = useTheme();
|
|
30
|
+
const { isInsider, searchEnabled, keyCreatedAt, rotateKey } = useAuth();
|
|
31
|
+
|
|
32
|
+
// Compute key age string
|
|
33
|
+
const keyAge = keyCreatedAt
|
|
34
|
+
? `${Math.floor((Date.now() - new Date(keyCreatedAt).getTime()) / 86_400_000)}d`
|
|
35
|
+
: null;
|
|
36
|
+
|
|
37
|
+
const fetchData = useCallback(async () => {
|
|
38
|
+
try {
|
|
39
|
+
const [jobsData, statsData] = await Promise.all([
|
|
40
|
+
getRunnerJobs(),
|
|
41
|
+
getRunnerStats(),
|
|
42
|
+
]);
|
|
43
|
+
setJobs(jobsData);
|
|
44
|
+
setStats(statsData);
|
|
45
|
+
setError(null);
|
|
46
|
+
} catch (err) {
|
|
47
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch runner data');
|
|
48
|
+
} finally {
|
|
49
|
+
setLoading(false);
|
|
50
|
+
}
|
|
51
|
+
}, []);
|
|
52
|
+
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
void fetchData();
|
|
55
|
+
}, [fetchData]);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
if (autoRefresh) {
|
|
59
|
+
intervalRef.current = setInterval(() => void fetchData(), REFRESH_INTERVAL);
|
|
60
|
+
}
|
|
61
|
+
return () => {
|
|
62
|
+
if (intervalRef.current) clearInterval(intervalRef.current);
|
|
63
|
+
};
|
|
64
|
+
}, [autoRefresh, fetchData]);
|
|
65
|
+
|
|
66
|
+
const handleRunNow = useCallback(async (id: string) => {
|
|
67
|
+
try {
|
|
68
|
+
await triggerJobRun(id);
|
|
69
|
+
await fetchData();
|
|
70
|
+
} catch {
|
|
71
|
+
await fetchData();
|
|
72
|
+
}
|
|
73
|
+
}, [fetchData]);
|
|
74
|
+
|
|
75
|
+
const handleSort = useCallback((column: SortColumn) => {
|
|
76
|
+
setSort((prev) => nextSort(prev, column));
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
const sortedJobs = useMemo(() => sortJobs(jobs, sort), [jobs, sort]);
|
|
80
|
+
|
|
81
|
+
const breadcrumbs = [{ label: 'Runner', path: 'runner' }];
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div className={`h-screen overflow-hidden ${theme === 'dark' ? 'dark' : ''}`}>
|
|
85
|
+
<div className="h-full flex flex-col bg-background text-foreground">
|
|
86
|
+
{/* Shared header */}
|
|
87
|
+
<Header
|
|
88
|
+
breadcrumbs={breadcrumbs}
|
|
89
|
+
isInsider={isInsider}
|
|
90
|
+
searchEnabled={searchEnabled}
|
|
91
|
+
theme={theme}
|
|
92
|
+
onToggleTheme={toggleTheme}
|
|
93
|
+
keyAge={keyAge}
|
|
94
|
+
onRotateKey={rotateKey}
|
|
95
|
+
/>
|
|
96
|
+
|
|
97
|
+
{/* Runner controls bar */}
|
|
98
|
+
<div className="shrink-0 max-w-6xl mx-auto w-full px-4 pt-2 pb-1 flex items-center justify-between">
|
|
99
|
+
{!loading && <StatsBar stats={stats} />}
|
|
100
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
101
|
+
<label className="flex items-center gap-1.5 text-xs text-muted-foreground cursor-pointer">
|
|
102
|
+
<input
|
|
103
|
+
type="checkbox"
|
|
104
|
+
checked={autoRefresh}
|
|
105
|
+
onChange={(e) => setAutoRefresh(e.target.checked)}
|
|
106
|
+
className="rounded"
|
|
107
|
+
/>
|
|
108
|
+
Auto-refresh
|
|
109
|
+
</label>
|
|
110
|
+
<Button
|
|
111
|
+
variant="ghost"
|
|
112
|
+
size="icon"
|
|
113
|
+
className="h-7 w-7"
|
|
114
|
+
title="Refresh now"
|
|
115
|
+
onClick={() => void fetchData()}
|
|
116
|
+
>
|
|
117
|
+
<RefreshCw className="h-3.5 w-3.5" />
|
|
118
|
+
</Button>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
{/* Error banner */}
|
|
123
|
+
{error && (
|
|
124
|
+
<div className="shrink-0 max-w-6xl mx-auto w-full px-4 pt-2">
|
|
125
|
+
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-400 px-4 py-3 rounded-lg text-sm">
|
|
126
|
+
{error}
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
)}
|
|
130
|
+
|
|
131
|
+
{loading ? (
|
|
132
|
+
<div className="flex-1 flex items-center justify-center text-muted-foreground">Loading...</div>
|
|
133
|
+
) : (
|
|
134
|
+
/* Single card: header pinned, body scrolls, scrollbar inside card */
|
|
135
|
+
<div className="flex-1 min-h-0 max-w-6xl mx-auto w-full px-4 pt-1 flex flex-col">
|
|
136
|
+
<div className="flex-1 min-h-0 bg-card border border-border rounded-lg flex flex-col">
|
|
137
|
+
{/* Pinned table header */}
|
|
138
|
+
<div className="shrink-0">
|
|
139
|
+
<JobTableHeader sort={sort} onSort={handleSort} />
|
|
140
|
+
</div>
|
|
141
|
+
{/* Scrollable table body — padding inside the scroll for mobile bottom space */}
|
|
142
|
+
<div className="flex-1 overflow-y-auto pb-32">
|
|
143
|
+
<JobTableBody jobs={sortedJobs} onRunNow={(id) => void handleRunNow(id)} />
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
)}
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runner job detail view — job info, enable/disable, run history.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ArrowLeft, Play, Power, PowerOff } from 'lucide-react';
|
|
6
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
7
|
+
import { Link, useParams } from 'react-router-dom';
|
|
8
|
+
|
|
9
|
+
import { Header } from '@/components/layout/Header';
|
|
10
|
+
import { RunHistory } from '@/components/runner/RunHistory';
|
|
11
|
+
import { StatusPill } from '@/components/runner/StatusPill';
|
|
12
|
+
import { Button } from '@/components/ui/button';
|
|
13
|
+
import { useAuth } from '@/lib/AuthContext';
|
|
14
|
+
import type { RunEntry, RunnerJob as RunnerJobType } from '@/lib/runner-api';
|
|
15
|
+
import {
|
|
16
|
+
disableJob,
|
|
17
|
+
enableJob,
|
|
18
|
+
getJobRuns,
|
|
19
|
+
getRunnerJob,
|
|
20
|
+
triggerJobRun,
|
|
21
|
+
} from '@/lib/runner-api';
|
|
22
|
+
import { useTheme } from '@/lib/theme';
|
|
23
|
+
|
|
24
|
+
export function RunnerJob() {
|
|
25
|
+
const { jobId } = useParams<{ jobId: string }>();
|
|
26
|
+
const [job, setJob] = useState<RunnerJobType | null>(null);
|
|
27
|
+
const [runs, setRuns] = useState<RunEntry[]>([]);
|
|
28
|
+
const [error, setError] = useState<string | null>(null);
|
|
29
|
+
const [loading, setLoading] = useState(true);
|
|
30
|
+
const [theme, toggleTheme] = useTheme();
|
|
31
|
+
const { isInsider, searchEnabled, keyCreatedAt, rotateKey } = useAuth();
|
|
32
|
+
|
|
33
|
+
const keyAge = keyCreatedAt
|
|
34
|
+
? `${Math.floor((Date.now() - new Date(keyCreatedAt).getTime()) / 86_400_000)}d`
|
|
35
|
+
: null;
|
|
36
|
+
|
|
37
|
+
const fetchData = useCallback(async () => {
|
|
38
|
+
if (!jobId) return;
|
|
39
|
+
try {
|
|
40
|
+
const [jobData, runsData] = await Promise.all([
|
|
41
|
+
getRunnerJob(jobId),
|
|
42
|
+
getJobRuns(jobId, 50),
|
|
43
|
+
]);
|
|
44
|
+
setJob(jobData);
|
|
45
|
+
setRuns(runsData);
|
|
46
|
+
setError(null);
|
|
47
|
+
} catch (err) {
|
|
48
|
+
setError(err instanceof Error ? err.message : 'Failed to fetch job');
|
|
49
|
+
} finally {
|
|
50
|
+
setLoading(false);
|
|
51
|
+
}
|
|
52
|
+
}, [jobId]);
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
void fetchData();
|
|
56
|
+
}, [fetchData]);
|
|
57
|
+
|
|
58
|
+
const handleToggleEnabled = useCallback(async () => {
|
|
59
|
+
if (!job) return;
|
|
60
|
+
try {
|
|
61
|
+
if (job.enabled) await disableJob(job.id);
|
|
62
|
+
else await enableJob(job.id);
|
|
63
|
+
await fetchData();
|
|
64
|
+
} catch {
|
|
65
|
+
await fetchData();
|
|
66
|
+
}
|
|
67
|
+
}, [job, fetchData]);
|
|
68
|
+
|
|
69
|
+
const handleRunNow = useCallback(async () => {
|
|
70
|
+
if (!job) return;
|
|
71
|
+
try {
|
|
72
|
+
await triggerJobRun(job.id);
|
|
73
|
+
setTimeout(() => void fetchData(), 500);
|
|
74
|
+
} catch {
|
|
75
|
+
await fetchData();
|
|
76
|
+
}
|
|
77
|
+
}, [job, fetchData]);
|
|
78
|
+
|
|
79
|
+
const breadcrumbs = [
|
|
80
|
+
{ label: 'Runner', path: 'runner' },
|
|
81
|
+
{ label: job?.name ?? jobId ?? '', path: `runner/${jobId}` },
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div className={`h-screen overflow-hidden ${theme === 'dark' ? 'dark' : ''}`}>
|
|
86
|
+
<div className="h-full flex flex-col bg-background text-foreground">
|
|
87
|
+
{/* Shared header */}
|
|
88
|
+
<Header
|
|
89
|
+
breadcrumbs={breadcrumbs}
|
|
90
|
+
isInsider={isInsider}
|
|
91
|
+
searchEnabled={searchEnabled}
|
|
92
|
+
theme={theme}
|
|
93
|
+
onToggleTheme={toggleTheme}
|
|
94
|
+
keyAge={keyAge}
|
|
95
|
+
onRotateKey={rotateKey}
|
|
96
|
+
/>
|
|
97
|
+
|
|
98
|
+
<div className="flex-1 overflow-y-auto max-w-4xl mx-auto px-4 py-6 pb-32 space-y-6 w-full">
|
|
99
|
+
<Link
|
|
100
|
+
to="/runner"
|
|
101
|
+
className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
102
|
+
>
|
|
103
|
+
<ArrowLeft className="h-4 w-4" />
|
|
104
|
+
Back to jobs
|
|
105
|
+
</Link>
|
|
106
|
+
|
|
107
|
+
{error && (
|
|
108
|
+
<div className="bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-400 px-4 py-3 rounded-lg text-sm">
|
|
109
|
+
{error}
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
{loading ? (
|
|
114
|
+
<div className="text-center py-12 text-muted-foreground">Loading...</div>
|
|
115
|
+
) : job ? (
|
|
116
|
+
<>
|
|
117
|
+
{/* Job Info Card */}
|
|
118
|
+
<div className="bg-card border border-border rounded-lg p-4 space-y-4">
|
|
119
|
+
<div className="flex items-start justify-between">
|
|
120
|
+
<div>
|
|
121
|
+
<h1 className="text-xl font-semibold">{job.name}</h1>
|
|
122
|
+
<div className="flex items-center gap-3 mt-2 text-sm text-muted-foreground">
|
|
123
|
+
<span>Type: <span className="text-foreground">{job.type}</span></span>
|
|
124
|
+
<span>Schedule: <code className="text-xs bg-muted px-1 py-0.5 rounded">{job.schedule}</code></span>
|
|
125
|
+
<span>Overlap: <span className="text-foreground">{job.overlapPolicy}</span></span>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
<StatusPill status={job.enabled ? job.status : 'disabled'} />
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div className="flex items-center gap-2 pt-2 border-t border-border">
|
|
132
|
+
<Button
|
|
133
|
+
variant="outline"
|
|
134
|
+
size="sm"
|
|
135
|
+
onClick={() => void handleToggleEnabled()}
|
|
136
|
+
className="gap-1.5"
|
|
137
|
+
>
|
|
138
|
+
{job.enabled
|
|
139
|
+
? <><PowerOff className="h-3.5 w-3.5" /> Disable</>
|
|
140
|
+
: <><Power className="h-3.5 w-3.5" /> Enable</>
|
|
141
|
+
}
|
|
142
|
+
</Button>
|
|
143
|
+
<Button
|
|
144
|
+
variant="outline"
|
|
145
|
+
size="sm"
|
|
146
|
+
onClick={() => void handleRunNow()}
|
|
147
|
+
className="gap-1.5"
|
|
148
|
+
>
|
|
149
|
+
<Play className="h-3.5 w-3.5" />
|
|
150
|
+
Run Now
|
|
151
|
+
</Button>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
|
|
155
|
+
{/* Run History */}
|
|
156
|
+
<div className="bg-card border border-border rounded-lg overflow-hidden">
|
|
157
|
+
<div className="px-4 py-3 border-b border-border">
|
|
158
|
+
<h2 className="text-sm font-medium text-muted-foreground">Run History</h2>
|
|
159
|
+
</div>
|
|
160
|
+
<RunHistory runs={runs} />
|
|
161
|
+
</div>
|
|
162
|
+
</>
|
|
163
|
+
) : (
|
|
164
|
+
<div className="text-center py-12 text-muted-foreground">Job not found</div>
|
|
165
|
+
)}
|
|
166
|
+
</div>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
4
|
+
"target": "ES2022",
|
|
5
|
+
"useDefineForClassFields": true,
|
|
6
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"types": ["vite/client"],
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
|
|
11
|
+
/* Bundler mode */
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"allowImportingTsExtensions": true,
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"moduleDetection": "force",
|
|
16
|
+
"noEmit": true,
|
|
17
|
+
"jsx": "react-jsx",
|
|
18
|
+
|
|
19
|
+
/* Linting */
|
|
20
|
+
"strict": true,
|
|
21
|
+
"noUnusedLocals": true,
|
|
22
|
+
"noUnusedParameters": true,
|
|
23
|
+
"erasableSyntaxOnly": true,
|
|
24
|
+
"noFallthroughCasesInSwitch": true,
|
|
25
|
+
"noUncheckedSideEffectImports": true,
|
|
26
|
+
"baseUrl": ".",
|
|
27
|
+
"paths": {
|
|
28
|
+
"@/*": ["./src/*"]
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"include": ["src"]
|
|
32
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "ES2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
"noUncheckedSideEffectImports": true
|
|
24
|
+
},
|
|
25
|
+
"include": ["vite.config.ts"]
|
|
26
|
+
}
|