diff --git a/understand-anything-plugin/packages/core/src/persistence/index.ts b/understand-anything-plugin/packages/core/src/persistence/index.ts index f14b011..d69857b 100644 --- a/understand-anything-plugin/packages/core/src/persistence/index.ts +++ b/understand-anything-plugin/packages/core/src/persistence/index.ts @@ -130,7 +130,7 @@ export function loadFingerprints(projectRoot: string): FingerprintStore | null { } } -const DEFAULT_CONFIG: ProjectConfig = { autoUpdate: false }; +const DEFAULT_CONFIG: ProjectConfig = { autoUpdate: false, outputLanguage: "en" }; export function saveConfig(projectRoot: string, config: ProjectConfig): void { const dir = ensureDir(projectRoot); diff --git a/understand-anything-plugin/packages/core/src/persistence/persistence.test.ts b/understand-anything-plugin/packages/core/src/persistence/persistence.test.ts index 02e0a1c..3a694fb 100644 --- a/understand-anything-plugin/packages/core/src/persistence/persistence.test.ts +++ b/understand-anything-plugin/packages/core/src/persistence/persistence.test.ts @@ -189,7 +189,7 @@ describe("persistence", () => { it("should return default config when no file exists", () => { const loaded = loadConfig(tempDir); - expect(loaded).toEqual({ autoUpdate: false }); + expect(loaded).toEqual({ autoUpdate: false, outputLanguage: "en" }); }); it("should return default config when config.json is corrupted", () => { @@ -198,7 +198,7 @@ describe("persistence", () => { writeFileSync(join(dir, "config.json"), "not json!!", "utf-8"); const loaded = loadConfig(tempDir); - expect(loaded).toEqual({ autoUpdate: false }); + expect(loaded).toEqual({ autoUpdate: false, outputLanguage: "en" }); }); }); }); diff --git a/understand-anything-plugin/packages/core/src/types.ts b/understand-anything-plugin/packages/core/src/types.ts index 890a9f0..b7a0fa6 100644 --- a/understand-anything-plugin/packages/core/src/types.ts +++ b/understand-anything-plugin/packages/core/src/types.ts @@ -113,9 +113,10 @@ export interface AnalysisMeta { theme?: ThemeConfig; } -// Project config (for auto-update opt-in) +// Project config (for auto-update opt-in and language preference) export interface ProjectConfig { autoUpdate: boolean; + outputLanguage?: string; } // Non-code structural sub-interfaces diff --git a/understand-anything-plugin/packages/dashboard/src/App.tsx b/understand-anything-plugin/packages/dashboard/src/App.tsx index 4aed9bf..ef41f9b 100644 --- a/understand-anything-plugin/packages/dashboard/src/App.tsx +++ b/understand-anything-plugin/packages/dashboard/src/App.tsx @@ -23,6 +23,7 @@ import type { KeyboardShortcut } from "./hooks/useKeyboardShortcuts"; import { ThemeProvider } from "./themes/index.ts"; import { ThemePicker } from "./components/ThemePicker.tsx"; import type { ThemeConfig } from "./themes/index.ts"; +import { I18nProvider } from "./contexts/I18nContext.tsx"; // Lazy-load heavy / optional components so they ship in separate chunks. const CodeViewer = lazy(() => import("./components/CodeViewer")); @@ -44,6 +45,7 @@ function dataUrl(fileName: string, token: string | null): string { "domain-graph.json": import.meta.env.VITE_DOMAIN_GRAPH_URL, "meta.json": import.meta.env.VITE_META_URL, "diff-overlay.json": import.meta.env.VITE_DIFF_OVERLAY_URL, + "config.json": import.meta.env.VITE_CONFIG_URL, }; const url = envMap[fileName]; if (url) return url; @@ -118,6 +120,7 @@ function Dashboard({ accessToken }: { accessToken: string }) { const [showKeyboardHelp, setShowKeyboardHelp] = useState(false); const [metaTheme, setMetaTheme] = useState(null); const [sidebarTab, setSidebarTab] = useState("info"); + const [outputLanguage, setOutputLanguage] = useState(); const viewMode = useDashboardStore((s) => s.viewMode); const setViewMode = useDashboardStore((s) => s.setViewMode); const isKnowledgeGraph = useDashboardStore((s) => s.isKnowledgeGraph); @@ -139,6 +142,12 @@ function Dashboard({ accessToken }: { accessToken: string }) { if (meta?.theme) setMetaTheme(meta.theme); }) .catch(() => {}); + fetch(dataUrl("config.json", accessToken)) + .then((r) => (r.ok ? r.json() : null)) + .then((config) => { + if (config?.outputLanguage) setOutputLanguage(config.outputLanguage); + }) + .catch(() => {}); }, []); useEffect(() => { @@ -399,7 +408,8 @@ function Dashboard({ accessToken }: { accessToken: string }) { } return ( - + +
{/* Header */}
@@ -662,6 +672,7 @@ function Dashboard({ accessToken }: { accessToken: string }) { )}
+
); } diff --git a/understand-anything-plugin/packages/dashboard/src/components/FileExplorer.tsx b/understand-anything-plugin/packages/dashboard/src/components/FileExplorer.tsx index a0697d1..e4940f6 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/FileExplorer.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/FileExplorer.tsx @@ -1,6 +1,7 @@ import { useMemo, useState } from "react"; import type { GraphNode } from "@understand-anything/core/types"; import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; interface FileEntry { name: string; @@ -142,6 +143,7 @@ export default function FileExplorer() { const graph = useDashboardStore((s) => s.graph); const openCodeViewer = useDashboardStore((s) => s.openCodeViewer); const navigateToNode = useDashboardStore((s) => s.navigateToNode); + const { t } = useI18n(); const entries = useMemo(() => buildFileTree(graph?.nodes ?? []), [graph]); const [expanded, setExpanded] = useState>(() => new Set()); @@ -176,7 +178,7 @@ export default function FileExplorer() { if (!graph) { return (
- No graph loaded + {t.common.noGraphLoaded}
); } @@ -185,15 +187,15 @@ export default function FileExplorer() {
- Analyzed Files + {t.fileExplorer.analyzedFiles}
- {totalFiles} files from the current knowledge graph + {totalFiles} {t.fileExplorer.filesFromGraph}
{entries.length === 0 ? ( -
No file paths found.
+
{t.fileExplorer.noFilePathsFound}
) : ( entries.map((entry) => ( s.graph); @@ -10,6 +11,7 @@ export default function FilterPanel() { const hasActiveFilters = useDashboardStore((s) => s.hasActiveFilters); const filterPanelOpen = useDashboardStore((s) => s.filterPanelOpen); const toggleFilterPanel = useDashboardStore((s) => s.toggleFilterPanel); + const { t } = useI18n(); const containerRef = useRef(null); @@ -97,7 +99,7 @@ export default function FilterPanel() { d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" /> - Filter + {t.common.filter} {filterPanelOpen && ( @@ -106,7 +108,7 @@ export default function FilterPanel() { {/* Node Types */}

- Node Types + {t.filterPanel.nodeTypes}

{allNodeTypes.map((type) => ( @@ -129,7 +131,7 @@ export default function FilterPanel() { {/* Complexity */}

- Complexity + {t.filterPanel.complexity}

{allComplexities.map((complexity) => ( @@ -153,7 +155,7 @@ export default function FilterPanel() { {layers.length > 0 && (

- Layers + {t.filterPanel.layers}

{layers.map((layer) => ( @@ -178,7 +180,7 @@ export default function FilterPanel() { {/* Edge Categories */}

- Edge Categories + {t.filterPanel.edgeCategories}

{allEdgeCategories.map((category) => ( @@ -206,7 +208,7 @@ export default function FilterPanel() { onClick={resetFilters} className="w-full px-3 py-1.5 text-sm bg-elevated hover:bg-gold/20 text-text-secondary hover:text-gold rounded-lg transition-colors" > - Reset All + {t.common.resetAll} )}
diff --git a/understand-anything-plugin/packages/dashboard/src/components/NodeInfo.tsx b/understand-anything-plugin/packages/dashboard/src/components/NodeInfo.tsx index eb954f4..2af4993 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/NodeInfo.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/NodeInfo.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; import type { NodeType, EdgeType, KnowledgeGraph, GraphNode } from "@understand-anything/core/types"; // Badge color classes keyed by NodeType — must be kept in sync with core NodeType union. @@ -33,56 +34,9 @@ const complexityBadgeColors: Record = { complex: "text-[#c97070] border border-[#c97070]/30 bg-[#c97070]/10", }; -/** - * Human-readable directional labels for all 29 edge types. - * Must be kept in sync with core EdgeType. - */ -const EDGE_LABELS: Record = { - imports: { forward: "imports", backward: "imported by" }, - exports: { forward: "exports to", backward: "exported by" }, - contains: { forward: "contains", backward: "contained in" }, - inherits: { forward: "inherits from", backward: "inherited by" }, - implements: { forward: "implements", backward: "implemented by" }, - calls: { forward: "calls", backward: "called by" }, - subscribes: { forward: "subscribes to", backward: "subscribed by" }, - publishes: { forward: "publishes to", backward: "consumed by" }, - middleware: { forward: "middleware for", backward: "uses middleware" }, - reads_from: { forward: "reads from", backward: "read by" }, - writes_to: { forward: "writes to", backward: "written by" }, - transforms: { forward: "transforms", backward: "transformed by" }, - validates: { forward: "validates", backward: "validated by" }, - depends_on: { forward: "depends on", backward: "depended on by" }, - tested_by: { forward: "tested by", backward: "tests" }, - configures: { forward: "configures", backward: "configured by" }, - related: { forward: "related to", backward: "related to" }, - similar_to: { forward: "similar to", backward: "similar to" }, - deploys: { forward: "deploys", backward: "deployed by" }, - serves: { forward: "serves", backward: "served by" }, - migrates: { forward: "migrates", backward: "migrated by" }, - documents: { forward: "documents", backward: "documented by" }, - provisions: { forward: "provisions", backward: "provisioned by" }, - routes: { forward: "routes to", backward: "routed from" }, - defines_schema: { forward: "defines schema for", backward: "schema defined by" }, - triggers: { forward: "triggers", backward: "triggered by" }, - contains_flow: { forward: "contains flow", backward: "flow in" }, - flow_step: { forward: "flow step", backward: "step of" }, - cross_domain: { forward: "cross-domain to", backward: "cross-domain from" }, - cites: { forward: "cites", backward: "cited by" }, - contradicts: { forward: "contradicts", backward: "contradicted by" }, - builds_on: { forward: "builds on", backward: "built upon by" }, - exemplifies: { forward: "exemplifies", backward: "exemplified by" }, - categorized_under: { forward: "categorized under", backward: "categorizes" }, - authored_by: { forward: "authored by", backward: "authored" }, -}; - -/** - * Returns a human-readable directional label for an edge type. - * Falls back to formatted type name for unknown edge types. - */ -function getDirectionalLabel(edgeType: string, isSource: boolean): string { - const labels = (EDGE_LABELS as Record)[edgeType]; +function getDirectionalLabel(edgeType: string, isSource: boolean, t: ReturnType["t"]): string { + const labels = t.edgeLabels[edgeType as EdgeType]; if (!labels) { - // Fallback for unknown edge types const formatted = edgeType.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); return isSource ? formatted : `${formatted} (reverse)`; } @@ -91,6 +45,7 @@ function getDirectionalLabel(edgeType: string, isSource: boolean): string { function KnowledgeNodeDetails({ node, graph }: { node: GraphNode; graph: KnowledgeGraph }) { const navigateToNode = useDashboardStore((s) => s.navigateToNode); + const { t } = useI18n(); const meta = node.knowledgeMeta; // Wikilinks (outgoing related edges) @@ -117,7 +72,7 @@ function KnowledgeNodeDetails({ node, graph }: { node: GraphNode; graph: Knowled
{categoryNode && (
-

Category

+

{t.nodeInfo.category}

{historyNodes.slice(-3).map((h, i, arr) => ( @@ -419,7 +376,7 @@ export default function NodeInfo() { : "text-text-muted border border-border-subtle hover:text-gold hover:border-gold/30" }`} > - {focusNodeId === node.id ? "Unfocus" : "Focus"} + {focusNodeId === node.id ? t.common.unfocus : t.common.focus}
@@ -431,7 +388,7 @@ export default function NodeInfo() {
-
File
+
{t.common.file}
{node.filePath} {node.lineRange && ( @@ -446,7 +403,7 @@ export default function NodeInfo() { onClick={() => openCodeViewer(node.id)} className="shrink-0 text-[10px] font-semibold uppercase tracking-wider px-2.5 py-1 rounded border border-accent/30 text-accent hover:text-accent-bright hover:border-accent/60 transition-colors" > - Open code + {t.common.openCode}
@@ -466,7 +423,7 @@ export default function NodeInfo() { > - Language Concepts + {t.nodeInfo.languageConcepts} {languageExpanded && (
@@ -481,7 +438,7 @@ export default function NodeInfo() { {node.tags.length > 0 && (

- Tags + {t.common.tags}

{node.tags.map((tag) => ( @@ -510,7 +467,7 @@ export default function NodeInfo() { {childNodes.length > 0 && (

- Defined in this file ({childNodes.length}) + {t.nodeInfo.definedInThisFile} ({childNodes.length})

{childNodes.map((child) => { @@ -548,14 +505,14 @@ export default function NodeInfo() { {otherConnections.length > 0 && (

- Connections ({otherConnections.length}) + {t.common.connections} ({otherConnections.length})

{otherConnections.map((edge, i) => { const isSource = edge.source === node.id; const otherId = isSource ? edge.target : edge.source; const otherNode = activeGraph?.nodes.find((n) => n.id === otherId); - const dirLabel = getDirectionalLabel(edge.type, isSource); + const dirLabel = getDirectionalLabel(edge.type, isSource, t); const arrow = isSource ? "\u2192" : "\u2190"; return ( diff --git a/understand-anything-plugin/packages/dashboard/src/components/PersonaSelector.tsx b/understand-anything-plugin/packages/dashboard/src/components/PersonaSelector.tsx index d496a7c..941e3a2 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/PersonaSelector.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/PersonaSelector.tsx @@ -1,27 +1,29 @@ import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; import type { Persona } from "../store"; -const personas: { id: Persona; label: string; description: string }[] = [ - { - id: "non-technical", - label: "Overview", - description: "High-level architecture view", - }, - { - id: "junior", - label: "Learn", - description: "Full dashboard with guided learning", - }, - { - id: "experienced", - label: "Deep Dive", - description: "Code-focused with chat", - }, -]; - export default function PersonaSelector() { const persona = useDashboardStore((s) => s.persona); const setPersona = useDashboardStore((s) => s.setPersona); + const { t } = useI18n(); + + const personas: { id: Persona; label: string; description: string }[] = [ + { + id: "non-technical", + label: t.personaSelector.overview, + description: t.personaSelector.overviewDesc, + }, + { + id: "junior", + label: t.personaSelector.learn, + description: t.personaSelector.learnDesc, + }, + { + id: "experienced", + label: t.personaSelector.deepDive, + description: t.personaSelector.deepDiveDesc, + }, + ]; return (
diff --git a/understand-anything-plugin/packages/dashboard/src/components/ProjectOverview.tsx b/understand-anything-plugin/packages/dashboard/src/components/ProjectOverview.tsx index 1760437..c9cb478 100644 --- a/understand-anything-plugin/packages/dashboard/src/components/ProjectOverview.tsx +++ b/understand-anything-plugin/packages/dashboard/src/components/ProjectOverview.tsx @@ -1,13 +1,15 @@ import { useDashboardStore } from "../store"; +import { useI18n } from "../contexts/I18nContext"; export default function ProjectOverview() { const graph = useDashboardStore((s) => s.graph); const startTour = useDashboardStore((s) => s.startTour); + const { t } = useI18n(); if (!graph) { return (
-

Loading project...

+

{t.common.loading}

); } @@ -15,13 +17,11 @@ export default function ProjectOverview() { const { project, nodes, edges, layers } = graph; const hasTour = graph.tour.length > 0; - // Count node types const typeCounts: Record = {}; for (const node of nodes) { typeCounts[node.type] = (typeCounts[node.type] ?? 0) + 1; } - // Count complexity const complexityCounts: Record = { simple: 0, moderate: 0, complex: 0 }; for (const node of nodes) { if (node.complexity) { @@ -29,7 +29,6 @@ export default function ProjectOverview() { } } - // Find top connected nodes const nodeConnections = new Map(); for (const edge of edges) { nodeConnections.set(edge.source, (nodeConnections.get(edge.source) ?? 0) + 1); @@ -45,16 +44,15 @@ export default function ProjectOverview() { const avgConnections = nodes.length > 0 ? (edges.length * 2 / nodes.length).toFixed(1) : "0"; - // Category breakdowns const categoryBreakdown = [ - { label: "Code", color: "var(--color-node-file)", count: (typeCounts["file"] ?? 0) + (typeCounts["function"] ?? 0) + (typeCounts["class"] ?? 0) + (typeCounts["module"] ?? 0) + (typeCounts["concept"] ?? 0) }, - { label: "Config", color: "var(--color-node-config)", count: typeCounts["config"] ?? 0 }, - { label: "Docs", color: "var(--color-node-document)", count: typeCounts["document"] ?? 0 }, - { label: "Infra", color: "var(--color-node-service)", count: (typeCounts["service"] ?? 0) + (typeCounts["resource"] ?? 0) + (typeCounts["pipeline"] ?? 0) }, - { label: "Data", color: "var(--color-node-table)", count: (typeCounts["table"] ?? 0) + (typeCounts["endpoint"] ?? 0) + (typeCounts["schema"] ?? 0) }, - { label: "Domain", color: "var(--color-node-concept)", count: (typeCounts["domain"] ?? 0) + (typeCounts["flow"] ?? 0) + (typeCounts["step"] ?? 0) }, + { label: t.projectOverview.code, color: "var(--color-node-file)", count: (typeCounts["file"] ?? 0) + (typeCounts["function"] ?? 0) + (typeCounts["class"] ?? 0) + (typeCounts["module"] ?? 0) + (typeCounts["concept"] ?? 0) }, + { label: t.projectOverview.config, color: "var(--color-node-config)", count: typeCounts["config"] ?? 0 }, + { label: t.projectOverview.docs, color: "var(--color-node-document)", count: typeCounts["document"] ?? 0 }, + { label: t.projectOverview.infra, color: "var(--color-node-service)", count: (typeCounts["service"] ?? 0) + (typeCounts["resource"] ?? 0) + (typeCounts["pipeline"] ?? 0) }, + { label: t.projectOverview.data, color: "var(--color-node-table)", count: (typeCounts["table"] ?? 0) + (typeCounts["endpoint"] ?? 0) + (typeCounts["schema"] ?? 0) }, + { label: t.projectOverview.domain, color: "var(--color-node-concept)", count: (typeCounts["domain"] ?? 0) + (typeCounts["flow"] ?? 0) + (typeCounts["step"] ?? 0) }, ]; - const hasNonCodeNodes = categoryBreakdown.some((c) => c.label !== "Code" && c.count > 0); + const hasNonCodeNodes = categoryBreakdown.some((c) => c.label !== t.projectOverview.code && c.count > 0); return (
@@ -66,26 +64,26 @@ export default function ProjectOverview() {
{nodes.length}
-
Nodes
+
{t.projectOverview.nodes}
{edges.length}
-
Edges
+
{t.projectOverview.edges}
{layers.length}
-
Layers
+
{t.projectOverview.layers}
{Object.keys(typeCounts).length}
-
Types
+
{t.projectOverview.types}
{/* File Types breakdown */} {hasNonCodeNodes && (
-

File Types

+

{t.projectOverview.fileTypes}

{categoryBreakdown.filter((c) => c.count > 0).map((cat) => (
@@ -104,7 +102,7 @@ export default function ProjectOverview() { {/* Languages */} {project.languages.length > 0 && (
-

Languages

+

{t.projectOverview.languages}

{project.languages.map((lang) => ( @@ -118,7 +116,7 @@ export default function ProjectOverview() { {/* Frameworks */} {project.frameworks.length > 0 && (
-

Frameworks

+

{t.projectOverview.frameworks}

{project.frameworks.map((fw) => ( @@ -131,7 +129,7 @@ export default function ProjectOverview() { {/* Node Type Breakdown */}
-

Node Type Distribution

+

{t.projectOverview.nodeTypeDistribution}

{Object.entries(typeCounts) .sort((a, b) => b[1] - a[1]) @@ -158,19 +156,19 @@ export default function ProjectOverview() { {/* Complexity Breakdown */} {Object.values(complexityCounts).some((c) => c > 0) && (
-

Complexity Distribution

+

{t.projectOverview.complexityDistribution}

{complexityCounts.simple}
-
Simple
+
{t.projectOverview.simple}
{complexityCounts.moderate}
-
Moderate
+
{t.projectOverview.moderate}
{complexityCounts.complex}
-
Complex
+
{t.projectOverview.complex}
@@ -179,7 +177,7 @@ export default function ProjectOverview() { {/* Top Connected Nodes */} {topNodes.length > 0 && (
-

Most Connected Nodes

+

{t.projectOverview.mostConnectedNodes}

{topNodes.map((node, idx) => (
- Avg Connections per Node + {t.projectOverview.avgConnectionsPerNode} {avgConnections}
{/* Analyzed at */}
- Analyzed: {new Date(project.analyzedAt).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })} + {t.common.analyzed}: {new Date(project.analyzedAt).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })}
{/* Start Tour button */} @@ -216,7 +214,7 @@ export default function ProjectOverview() { onClick={startTour} className="w-full bg-accent/10 border border-accent/30 text-accent text-sm font-medium py-2.5 px-4 rounded-lg hover:bg-accent/20 transition-all duration-200" > - Start Guided Tour + {t.common.startGuidedTour} )}
diff --git a/understand-anything-plugin/packages/dashboard/src/contexts/I18nContext.tsx b/understand-anything-plugin/packages/dashboard/src/contexts/I18nContext.tsx new file mode 100644 index 0000000..08adb7b --- /dev/null +++ b/understand-anything-plugin/packages/dashboard/src/contexts/I18nContext.tsx @@ -0,0 +1,44 @@ +import { createContext, useContext, useMemo, type ReactNode } from "react"; +import { getLocale, resolveLocaleKey, type Locale, type LocaleKey } from "../locales"; + +interface I18nContextValue { + locale: Locale; + localeKey: LocaleKey; + t: Locale; +} + +const I18nContext = createContext(null); + +export function useI18n(): I18nContextValue { + const ctx = useContext(I18nContext); + if (!ctx) { + throw new Error("useI18n must be used within an I18nProvider"); + } + return ctx; +} + +export function I18nProvider({ + language, + children, +}: { + language?: string; + children: ReactNode; +}) { + const localeKey = useMemo(() => resolveLocaleKey(language), [language]); + const locale = useMemo(() => getLocale(localeKey), [localeKey]); + + const value = useMemo( + () => ({ + locale, + localeKey, + t: locale, + }), + [locale, localeKey] + ); + + return ( + + {children} + + ); +} \ No newline at end of file diff --git a/understand-anything-plugin/packages/dashboard/src/locales/en.ts b/understand-anything-plugin/packages/dashboard/src/locales/en.ts new file mode 100644 index 0000000..0655e89 --- /dev/null +++ b/understand-anything-plugin/packages/dashboard/src/locales/en.ts @@ -0,0 +1,115 @@ +export const en = { + common: { + loading: "Loading project...", + noGraphLoaded: "No graph loaded", + selectNode: "Select a node to see details", + back: "Back", + focus: "Focus", + unfocus: "Unfocus", + openCode: "Open code", + file: "File", + tags: "Tags", + connections: "Connections", + filter: "Filter", + resetAll: "Reset All", + analyzed: "Analyzed", + startGuidedTour: "Start Guided Tour", + truncated: "(truncated)", + preview: "Preview", + doubleClickToOpen: "double-click to open", + }, + projectOverview: { + nodes: "Nodes", + edges: "Edges", + layers: "Layers", + types: "Types", + fileTypes: "File Types", + code: "Code", + config: "Config", + docs: "Docs", + infra: "Infra", + data: "Data", + domain: "Domain", + languages: "Languages", + frameworks: "Frameworks", + nodeTypeDistribution: "Node Type Distribution", + complexityDistribution: "Complexity Distribution", + simple: "Simple", + moderate: "Moderate", + complex: "Complex", + mostConnectedNodes: "Most Connected Nodes", + avgConnectionsPerNode: "Avg Connections per Node", + }, + nodeInfo: { + definedInThisFile: "Defined in this file", + languageConcepts: "Language Concepts", + category: "Category", + wikilinks: "Wikilinks", + backlinks: "Backlinks", + entities: "Entities", + businessRules: "Business Rules", + crossDomain: "Cross-Domain", + flows: "Flows", + entryPoint: "Entry Point", + steps: "Steps", + implementation: "Implementation", + }, + fileExplorer: { + analyzedFiles: "Analyzed Files", + filesFromGraph: "files from the current knowledge graph", + noFilePathsFound: "No file paths found.", + }, + filterPanel: { + nodeTypes: "Node Types", + complexity: "Complexity", + layers: "Layers", + edgeCategories: "Edge Categories", + }, + personaSelector: { + overview: "Overview", + overviewDesc: "High-level architecture view", + learn: "Learn", + learnDesc: "Full dashboard with guided learning", + deepDive: "Deep Dive", + deepDiveDesc: "Code-focused with chat", + }, + edgeLabels: { + imports: { forward: "imports", backward: "imported by" }, + exports: { forward: "exports to", backward: "exported by" }, + contains: { forward: "contains", backward: "contained in" }, + inherits: { forward: "inherits from", backward: "inherited by" }, + implements: { forward: "implements", backward: "implemented by" }, + calls: { forward: "calls", backward: "called by" }, + subscribes: { forward: "subscribes to", backward: "subscribed by" }, + publishes: { forward: "publishes to", backward: "consumed by" }, + middleware: { forward: "middleware for", backward: "uses middleware" }, + reads_from: { forward: "reads from", backward: "read by" }, + writes_to: { forward: "writes to", backward: "written by" }, + transforms: { forward: "transforms", backward: "transformed by" }, + validates: { forward: "validates", backward: "validated by" }, + depends_on: { forward: "depends on", backward: "depended on by" }, + tested_by: { forward: "tested by", backward: "tests" }, + configures: { forward: "configures", backward: "configured by" }, + related: { forward: "related to", backward: "related to" }, + similar_to: { forward: "similar to", backward: "similar to" }, + deploys: { forward: "deploys", backward: "deployed by" }, + serves: { forward: "serves", backward: "served by" }, + migrates: { forward: "migrates", backward: "migrated by" }, + documents: { forward: "documents", backward: "documented by" }, + provisions: { forward: "provisions", backward: "provisioned by" }, + routes: { forward: "routes to", backward: "routed from" }, + defines_schema: { forward: "defines schema for", backward: "schema defined by" }, + triggers: { forward: "triggers", backward: "triggered by" }, + contains_flow: { forward: "contains flow", backward: "flow in" }, + flow_step: { forward: "flow step", backward: "step of" }, + cross_domain: { forward: "cross-domain to", backward: "cross-domain from" }, + cites: { forward: "cites", backward: "cited by" }, + contradicts: { forward: "contradicts", backward: "contradicted by" }, + builds_on: { forward: "builds on", backward: "built upon by" }, + exemplifies: { forward: "exemplifies", backward: "exemplified by" }, + categorized_under: { forward: "categorized under", backward: "categorizes" }, + authored_by: { forward: "authored by", backward: "authored" }, + }, +}; + +export default en; \ No newline at end of file diff --git a/understand-anything-plugin/packages/dashboard/src/locales/index.ts b/understand-anything-plugin/packages/dashboard/src/locales/index.ts new file mode 100644 index 0000000..911861b --- /dev/null +++ b/understand-anything-plugin/packages/dashboard/src/locales/index.ts @@ -0,0 +1,32 @@ +import en from "./en"; +import zh from "./zh"; +import zhTW from "./zh-TW"; +import ja from "./ja"; +import ko from "./ko"; + +export type LocaleKey = "en" | "zh" | "zh-TW" | "ja" | "ko"; +export type Locale = typeof en; + +export const locales: Record = { + en, + zh, + "zh-TW": zhTW, + ja, + ko, +}; + +export function getLocale(key: LocaleKey): Locale { + return locales[key] ?? locales.en; +} + +export function resolveLocaleKey(lang: string | undefined): LocaleKey { + if (!lang) return "en"; + const normalized = lang.toLowerCase().replace(/[_\s]/g, "-"); + if (normalized === "zh" || normalized === "chinese" || normalized === "zh-cn") return "zh"; + if (normalized === "zh-tw" || normalized === "traditional-chinese") return "zh-TW"; + if (normalized === "ja" || normalized === "japanese") return "ja"; + if (normalized === "ko" || normalized === "korean") return "ko"; + return "en"; +} + +export { en, zh, zhTW as "zh-TW", ja, ko }; \ No newline at end of file diff --git a/understand-anything-plugin/packages/dashboard/src/locales/ja.ts b/understand-anything-plugin/packages/dashboard/src/locales/ja.ts new file mode 100644 index 0000000..01bb52e --- /dev/null +++ b/understand-anything-plugin/packages/dashboard/src/locales/ja.ts @@ -0,0 +1,115 @@ +export const ja = { + common: { + loading: "プロジェクトを読み込み中...", + noGraphLoaded: "知識グラフが読み込まれていません", + selectNode: "ノードを選択して詳細を表示", + back: "戻る", + focus: "フォーカス", + unfocus: "フォーカス解除", + openCode: "コードを開く", + file: "ファイル", + tags: "タグ", + connections: "接続", + filter: "フィルター", + resetAll: "すべてリセット", + analyzed: "分析日時", + startGuidedTour: "ガイド付きツアーを開始", + truncated: "(省略)", + preview: "プレビュー", + doubleClickToOpen: "ダブルクリックで開く", + }, + projectOverview: { + nodes: "ノード", + edges: "エッジ", + layers: "レイヤー", + types: "タイプ", + fileTypes: "ファイルタイプ", + code: "コード", + config: "設定", + docs: "ドキュメント", + infra: "インフラ", + data: "データ", + domain: "ドメイン", + languages: "プログラミング言語", + frameworks: "フレームワーク", + nodeTypeDistribution: "ノードタイプ分布", + complexityDistribution: "複雑度分布", + simple: "単純", + moderate: "中程度", + complex: "複雑", + mostConnectedNodes: "最も接続されているノード", + avgConnectionsPerNode: "ノード平均接続数", + }, + nodeInfo: { + definedInThisFile: "このファイルで定義", + languageConcepts: "言語概念", + category: "カテゴリ", + wikilinks: "Wikilinks", + backlinks: "Backlinks", + entities: "エンティティ", + businessRules: "ビジネスルール", + crossDomain: "クロスドメイン", + flows: "フロー", + entryPoint: "エントリポイント", + steps: "ステップ", + implementation: "実装", + }, + fileExplorer: { + analyzedFiles: "分析済みファイル", + filesFromGraph: "現在の知識グラフからのファイル", + noFilePathsFound: "ファイルパスが見つかりません。", + }, + filterPanel: { + nodeTypes: "ノードタイプ", + complexity: "複雑度", + layers: "レイヤー", + edgeCategories: "エッジカテゴリ", + }, + personaSelector: { + overview: "概要", + overviewDesc: "高レベルアーキテクチャビュー", + learn: "学習", + learnDesc: "ガイド付き学習付き完全ダッシュボード", + deepDive: "詳細", + deepDiveDesc: "コード中心のチャット", + }, + edgeLabels: { + imports: { forward: "インポート", backward: "インポートされる" }, + exports: { forward: "エクスポート", backward: "エクスポートされる" }, + contains: { forward: "含む", backward: "含まれる" }, + inherits: { forward: "継承", backward: "継承される" }, + implements: { forward: "実装", backward: "実装される" }, + calls: { forward: "呼び出す", backward: "呼び出される" }, + subscribes: { forward: "購読", backward: "購読される" }, + publishes: { forward: "公開", backward: "消費される" }, + middleware: { forward: "ミドルウェア", backward: "ミドルウェアを使用" }, + reads_from: { forward: "読み取り", backward: "読み取られる" }, + writes_to: { forward: "書き込み", backward: "書き込まれる" }, + transforms: { forward: "変換", backward: "変換される" }, + validates: { forward: "検証", backward: "検証される" }, + depends_on: { forward: "依存", backward: "依存される" }, + tested_by: { forward: "テストされる", backward: "テスト" }, + configures: { forward: "設定", backward: "設定される" }, + related: { forward: "関連", backward: "関連" }, + similar_to: { forward: "類似", backward: "類似" }, + deploys: { forward: "デプロイ", backward: "デプロイされる" }, + serves: { forward: "提供", backward: "提供される" }, + migrates: { forward: "移行", backward: "移行される" }, + documents: { forward: "ドキュメント化", backward: "ドキュメント化される" }, + provisions: { forward: "提供", backward: "提供される" }, + routes: { forward: "ルーティング", backward: "ルーティングされる" }, + defines_schema: { forward: "スキーマ定義", backward: "スキーマ定義される" }, + triggers: { forward: "トリガー", backward: "トリガーされる" }, + contains_flow: { forward: "フローを含む", backward: "フロー内" }, + flow_step: { forward: "フローステップ", backward: "ステップの" }, + cross_domain: { forward: "クロスドメイン", backward: "クロスドメインから" }, + cites: { forward: "引用", backward: "引用される" }, + contradicts: { forward: "矛盾", backward: "矛盾される" }, + builds_on: { forward: "基礎", backward: "基礎となる" }, + exemplifies: { forward: "例示", backward: "例示される" }, + categorized_under: { forward: "カテゴリ化", backward: "カテゴリ化する" }, + authored_by: { forward: "作成者", backward: "作成" }, + }, +}; + +export default ja; \ No newline at end of file diff --git a/understand-anything-plugin/packages/dashboard/src/locales/ko.ts b/understand-anything-plugin/packages/dashboard/src/locales/ko.ts new file mode 100644 index 0000000..6e1dc88 --- /dev/null +++ b/understand-anything-plugin/packages/dashboard/src/locales/ko.ts @@ -0,0 +1,115 @@ +export const ko = { + common: { + loading: "프로젝트 로딩 중...", + noGraphLoaded: "지식 그래프가 로드되지 않음", + selectNode: "노드를 선택하여 상세 정보 확인", + back: "뒤로", + focus: "포커스", + unfocus: "포커스 해제", + openCode: "코드 열기", + file: "파일", + tags: "태그", + connections: "연결", + filter: "필터", + resetAll: "모두 재설정", + analyzed: "분석 시간", + startGuidedTour: "가이드 투어 시작", + truncated: "(생략)", + preview: "미리보기", + doubleClickToOpen: "두 번 클릭하여 열기", + }, + projectOverview: { + nodes: "노드", + edges: "엣지", + layers: "레이어", + types: "타입", + fileTypes: "파일 타입", + code: "코드", + config: "설정", + docs: "문서", + infra: "인프라", + data: "데이터", + domain: "도메인", + languages: "프로그래밍 언어", + frameworks: "프레임워크", + nodeTypeDistribution: "노드 타입 분포", + complexityDistribution: "복잡도 분포", + simple: "단순", + moderate: "중간", + complex: "복잡", + mostConnectedNodes: "가장 많이 연결된 노드", + avgConnectionsPerNode: "노드 평균 연결 수", + }, + nodeInfo: { + definedInThisFile: "이 파일에 정義", + languageConcepts: "언어 개념", + category: "카테고리", + wikilinks: "Wikilinks", + backlinks: "Backlinks", + entities: "엔티티", + businessRules: "비즈니스 규칙", + crossDomain: "크로스 도메인", + flows: "플로우", + entryPoint: "진입점", + steps: "단계", + implementation: "구현", + }, + fileExplorer: { + analyzedFiles: "분석된 파일", + filesFromGraph: "현재 지식 그래프의 파일", + noFilePathsFound: "파일 경로를 찾을 수 없습니다.", + }, + filterPanel: { + nodeTypes: "노드 타입", + complexity: "복잡도", + layers: "레이어", + edgeCategories: "엣지 카테고리", + }, + personaSelector: { + overview: "개요", + overviewDesc: "고수준 아키텍처 뷰", + learn: "학습", + learnDesc: "가이드 학습 포함 완전 대시보드", + deepDive: "심층", + deepDiveDesc: "코드 중심 채팅", + }, + edgeLabels: { + imports: { forward: "임포트", backward: "임포트됨" }, + exports: { forward: "내보내기", backward: "내보내기됨" }, + contains: { forward: "포함", backward: "포함됨" }, + inherits: { forward: "상속", backward: "상속됨" }, + implements: { forward: "구현", backward: "구현됨" }, + calls: { forward: "호출", backward: "호출됨" }, + subscribes: { forward: "구독", backward: "구독됨" }, + publishes: { forward: "게시", backward: "소비됨" }, + middleware: { forward: "미들웨어", backward: "미들웨어 사용" }, + reads_from: { forward: "읽기", backward: "읽기됨" }, + writes_to: { forward: "쓰기", backward: "쓰기됨" }, + transforms: { forward: "변환", backward: "변환됨" }, + validates: { forward: "검증", backward: "검증됨" }, + depends_on: { forward: "종속", backward: "종속됨" }, + tested_by: { forward: "테스트됨", backward: "테스트" }, + configures: { forward: "설정", backward: "설정됨" }, + related: { forward: "관련", backward: "관련" }, + similar_to: { forward: "유사", backward: "유사" }, + deploys: { forward: "배포", backward: "배포됨" }, + serves: { forward: "서비스", backward: "서비스됨" }, + migrates: { forward: "마이그레이션", backward: "마이그레이션됨" }, + documents: { forward: "문서화", backward: "문서화됨" }, + provisions: { forward: "제공", backward: "제공됨" }, + routes: { forward: "라우팅", backward: "라우팅됨" }, + defines_schema: { forward: "스키마 정의", backward: "스키마 정義됨" }, + triggers: { forward: "트리거", backward: "트리거됨" }, + contains_flow: { forward: "플로우 포함", backward: "플로우 내" }, + flow_step: { forward: "플로우 단계", backward: "단계의" }, + cross_domain: { forward: "크로스 도메인", backward: "크로스 도메인에서" }, + cites: { forward: "인용", backward: "인용됨" }, + contradicts: { forward: "반박", backward: "반박됨" }, + builds_on: { forward: "기반", backward: "기반됨" }, + exemplifies: { forward: "예시", backward: "예시됨" }, + categorized_under: { forward: "카테고리화", backward: "카테고리화함" }, + authored_by: { forward: "작성자", backward: "작성" }, + }, +}; + +export default ko; \ No newline at end of file diff --git a/understand-anything-plugin/packages/dashboard/src/locales/zh-TW.ts b/understand-anything-plugin/packages/dashboard/src/locales/zh-TW.ts new file mode 100644 index 0000000..f8c6f01 --- /dev/null +++ b/understand-anything-plugin/packages/dashboard/src/locales/zh-TW.ts @@ -0,0 +1,115 @@ +export const zhTW = { + common: { + loading: "載入專案...", + noGraphLoaded: "未載入知識圖谱", + selectNode: "選擇節點查看詳情", + back: "返回", + focus: "聚焦", + unfocus: "取消聚焦", + openCode: "開啟程式碼", + file: "檔案", + tags: "標籤", + connections: "連結", + filter: "篩選", + resetAll: "重置全部", + analyzed: "分析時間", + startGuidedTour: "開始導覽", + truncated: "(已截斷)", + preview: "預覽", + doubleClickToOpen: "雙擊開啟", + }, + projectOverview: { + nodes: "節點", + edges: "邊", + layers: "層級", + types: "類型", + fileTypes: "檔案類型", + code: "程式碼", + config: "配置", + docs: "文件", + infra: "基礎設施", + data: "資料", + domain: "領域", + languages: "程式語言", + frameworks: "框架", + nodeTypeDistribution: "節點類型分布", + complexityDistribution: "複雜度分布", + simple: "簡單", + moderate: "中等", + complex: "複雜", + mostConnectedNodes: "連結最多的節點", + avgConnectionsPerNode: "節點平均連結數", + }, + nodeInfo: { + definedInThisFile: "在此檔案中定義", + languageConcepts: "語言概念", + category: "分類", + wikilinks: "維基連結", + backlinks: "反向連結", + entities: "實體", + businessRules: "業務規則", + crossDomain: "跨領域", + flows: "流程", + entryPoint: "入口點", + steps: "步驟", + implementation: "實作", + }, + fileExplorer: { + analyzedFiles: "已分析檔案", + filesFromGraph: "來自目前知識圖谱的檔案", + noFilePathsFound: "未找到檔案路徑。", + }, + filterPanel: { + nodeTypes: "節點類型", + complexity: "複雜度", + layers: "層級", + edgeCategories: "邊類別", + }, + personaSelector: { + overview: "概覽", + overviewDesc: "高層次架構視圖", + learn: "學習", + learnDesc: "完整儀表板與導覽學習", + deepDive: "深入", + deepDiveDesc: "程式碼聚焦與對話", + }, + edgeLabels: { + imports: { forward: "導入", backward: "被導入" }, + exports: { forward: "導出到", backward: "被導出" }, + contains: { forward: "包含", backward: "被包含" }, + inherits: { forward: "繼承自", backward: "被繼承" }, + implements: { forward: "實作", backward: "被實作" }, + calls: { forward: "呼叫", backward: "被呼叫" }, + subscribes: { forward: "訂閱", backward: "被訂閱" }, + publishes: { forward: "發布到", backward: "被消費" }, + middleware: { forward: "中介軟體", backward: "使用中介軟體" }, + reads_from: { forward: "讀取", backward: "被讀取" }, + writes_to: { forward: "寫入", backward: "被寫入" }, + transforms: { forward: "轉換", backward: "被轉換" }, + validates: { forward: "驗證", backward: "被驗證" }, + depends_on: { forward: "依賴", backward: "被依賴" }, + tested_by: { forward: "被測試", backward: "測試" }, + configures: { forward: "配置", backward: "被配置" }, + related: { forward: "相關", backward: "相關" }, + similar_to: { forward: "相似", backward: "相似" }, + deploys: { forward: "部署", backward: "被部署" }, + serves: { forward: "服務", backward: "被服務" }, + migrates: { forward: "遷移", backward: "被遷移" }, + documents: { forward: "文件化", backward: "被文件化" }, + provisions: { forward: "提供", backward: "被提供" }, + routes: { forward: "路由到", backward: "被路由" }, + defines_schema: { forward: "定義架構", backward: "架構被定義" }, + triggers: { forward: "觸發", backward: "被觸發" }, + contains_flow: { forward: "包含流程", backward: "流程所在" }, + flow_step: { forward: "流程步驟", backward: "步驟所属" }, + cross_domain: { forward: "跨領域到", backward: "跨領域来自" }, + cites: { forward: "引用", backward: "被引用" }, + contradicts: { forward: "反駁", backward: "被反駁" }, + builds_on: { forward: "基於", backward: "作為基礎" }, + exemplifies: { forward: "例證", backward: "被例證" }, + categorized_under: { forward: "归类於", backward: "归类" }, + authored_by: { forward: "作者", backward: "著作" }, + }, +}; + +export default zhTW; \ No newline at end of file diff --git a/understand-anything-plugin/packages/dashboard/src/locales/zh.ts b/understand-anything-plugin/packages/dashboard/src/locales/zh.ts new file mode 100644 index 0000000..00df8ec --- /dev/null +++ b/understand-anything-plugin/packages/dashboard/src/locales/zh.ts @@ -0,0 +1,115 @@ +export const zh = { + common: { + loading: "加载项目...", + noGraphLoaded: "未加载知识图谱", + selectNode: "选择节点查看详情", + back: "返回", + focus: "聚焦", + unfocus: "取消聚焦", + openCode: "打开代码", + file: "文件", + tags: "标签", + connections: "连接", + filter: "筛选", + resetAll: "重置全部", + analyzed: "分析时间", + startGuidedTour: "开始导览", + truncated: "(已截断)", + preview: "预览", + doubleClickToOpen: "双击打开", + }, + projectOverview: { + nodes: "节点", + edges: "边", + layers: "层级", + types: "类型", + fileTypes: "文件类型", + code: "代码", + config: "配置", + docs: "文档", + infra: "基础设施", + data: "数据", + domain: "领域", + languages: "编程语言", + frameworks: "框架", + nodeTypeDistribution: "节点类型分布", + complexityDistribution: "复杂度分布", + simple: "简单", + moderate: "中等", + complex: "复杂", + mostConnectedNodes: "连接最多的节点", + avgConnectionsPerNode: "节点平均连接数", + }, + nodeInfo: { + definedInThisFile: "在此文件中定义", + languageConcepts: "语言概念", + category: "分类", + wikilinks: "维基链接", + backlinks: "反向链接", + entities: "实体", + businessRules: "业务规则", + crossDomain: "跨领域", + flows: "流程", + entryPoint: "入口点", + steps: "步骤", + implementation: "实现", + }, + fileExplorer: { + analyzedFiles: "已分析文件", + filesFromGraph: "来自当前知识图谱的文件", + noFilePathsFound: "未找到文件路径。", + }, + filterPanel: { + nodeTypes: "节点类型", + complexity: "复杂度", + layers: "层级", + edgeCategories: "边类别", + }, + personaSelector: { + overview: "概览", + overviewDesc: "高层次架构视图", + learn: "学习", + learnDesc: "完整仪表盘与导览学习", + deepDive: "深入", + deepDiveDesc: "代码聚焦与对话", + }, + edgeLabels: { + imports: { forward: "导入", backward: "被导入" }, + exports: { forward: "导出到", backward: "被导出" }, + contains: { forward: "包含", backward: "被包含" }, + inherits: { forward: "继承自", backward: "被继承" }, + implements: { forward: "实现", backward: "被实现" }, + calls: { forward: "调用", backward: "被调用" }, + subscribes: { forward: "订阅", backward: "被订阅" }, + publishes: { forward: "发布到", backward: "被消费" }, + middleware: { forward: "中间件", backward: "使用中间件" }, + reads_from: { forward: "读取", backward: "被读取" }, + writes_to: { forward: "写入", backward: "被写入" }, + transforms: { forward: "转换", backward: "被转换" }, + validates: { forward: "验证", backward: "被验证" }, + depends_on: { forward: "依赖", backward: "被依赖" }, + tested_by: { forward: "被测试", backward: "测试" }, + configures: { forward: "配置", backward: "被配置" }, + related: { forward: "相关", backward: "相关" }, + similar_to: { forward: "相似", backward: "相似" }, + deploys: { forward: "部署", backward: "被部署" }, + serves: { forward: "服务", backward: "被服务" }, + migrates: { forward: "迁移", backward: "被迁移" }, + documents: { forward: "文档化", backward: "被文档化" }, + provisions: { forward: "提供", backward: "被提供" }, + routes: { forward: "路由到", backward: "被路由" }, + defines_schema: { forward: "定义架构", backward: "架构被定义" }, + triggers: { forward: "触发", backward: "被触发" }, + contains_flow: { forward: "包含流程", backward: "流程所在" }, + flow_step: { forward: "流程步骤", backward: "步骤所属" }, + cross_domain: { forward: "跨领域到", backward: "跨领域来自" }, + cites: { forward: "引用", backward: "被引用" }, + contradicts: { forward: "反驳", backward: "被反驳" }, + builds_on: { forward: "基于", backward: "作为基础" }, + exemplifies: { forward: "例证", backward: "被例证" }, + categorized_under: { forward: "归类于", backward: "归类" }, + authored_by: { forward: "作者", backward: "著作" }, + }, +}; + +export default zh; \ No newline at end of file diff --git a/understand-anything-plugin/packages/dashboard/vite.config.ts b/understand-anything-plugin/packages/dashboard/vite.config.ts index c28056b..3e097da 100644 --- a/understand-anything-plugin/packages/dashboard/vite.config.ts +++ b/understand-anything-plugin/packages/dashboard/vite.config.ts @@ -252,6 +252,7 @@ export default defineConfig({ pathname === "/domain-graph.json" || pathname === "/diff-overlay.json" || pathname === "/meta.json" || + pathname === "/config.json" || pathname === "/file-content.json"; if (!isProtectedEndpoint) { @@ -272,6 +273,24 @@ export default defineConfig({ return; } + if (pathname === "/config.json") { + const configCandidates = graphFileCandidates("config.json"); + for (const candidate of configCandidates) { + if (fs.existsSync(candidate)) { + try { + const raw = JSON.parse(fs.readFileSync(candidate, "utf-8")); + sendJson(res, 200, raw); + return; + } catch { + sendJson(res, 500, { error: "Failed to read config file" }); + return; + } + } + } + sendJson(res, 200, { autoUpdate: false, outputLanguage: "en" }); + return; + } + const fileName = pathname === "/diff-overlay.json" ? "diff-overlay.json" diff --git a/understand-anything-plugin/pnpm-workspace.yaml b/understand-anything-plugin/pnpm-workspace.yaml index dee51e9..901bea1 100644 --- a/understand-anything-plugin/pnpm-workspace.yaml +++ b/understand-anything-plugin/pnpm-workspace.yaml @@ -1,2 +1,15 @@ packages: - "packages/*" +allowBuilds: + esbuild: set this to true or false + tree-sitter-c: set this to true or false + tree-sitter-c-sharp: set this to true or false + tree-sitter-cpp: set this to true or false + tree-sitter-go: set this to true or false + tree-sitter-java: set this to true or false + tree-sitter-javascript: set this to true or false + tree-sitter-php: set this to true or false + tree-sitter-python: set this to true or false + tree-sitter-ruby: set this to true or false + tree-sitter-rust: set this to true or false + tree-sitter-typescript: set this to true or false