mirror of
https://github.com/aljazceru/claude-code-viewer.git
synced 2025-12-31 04:04:21 +01:00
chore: inject global claude directory by using GLOBAL_CLAUDE_DIR environment variables
This commit is contained in:
@@ -1,12 +1,15 @@
|
||||
import type { SSEEventMap } from "../../types/sse";
|
||||
|
||||
export const callSSE = () => {
|
||||
export const callSSE = (options?: { onOpen?: (event: Event) => void }) => {
|
||||
const { onOpen } = options ?? {};
|
||||
|
||||
const eventSource = new EventSource(
|
||||
new URL("/api/sse", window.location.origin).href,
|
||||
);
|
||||
|
||||
const handleOnOpen = (event: Event) => {
|
||||
console.log("SSE connection opened", event);
|
||||
onOpen?.(event);
|
||||
};
|
||||
|
||||
eventSource.onopen = handleOnOpen;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useAtom } from "jotai";
|
||||
import {
|
||||
type FC,
|
||||
@@ -7,6 +8,7 @@ import {
|
||||
useRef,
|
||||
} from "react";
|
||||
import type { SSEEvent } from "../../../types/sse";
|
||||
import { projectListQuery } from "../../api/queries";
|
||||
import { callSSE } from "../callSSE";
|
||||
import {
|
||||
type EventListener,
|
||||
@@ -21,9 +23,18 @@ export const ServerEventsProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
Map<SSEEvent["kind"], Set<(event: SSEEvent) => void>>
|
||||
>(new Map());
|
||||
const [, setSSEState] = useAtom(sseAtom);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useEffect(() => {
|
||||
const sse = callSSE();
|
||||
const sse = callSSE({
|
||||
onOpen: async () => {
|
||||
// reconnect 中のイベントは購読できないので
|
||||
// open 時にまとめて invalidate する
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: projectListQuery.queryKey,
|
||||
});
|
||||
},
|
||||
});
|
||||
sseRef.current = sse;
|
||||
|
||||
const { removeEventListener } = sse.addEventListener("connect", (event) => {
|
||||
@@ -39,7 +50,7 @@ export const ServerEventsProvider: FC<PropsWithChildren> = ({ children }) => {
|
||||
sse.cleanUp();
|
||||
removeEventListener();
|
||||
};
|
||||
}, [setSSEState]);
|
||||
}, [setSSEState, queryClient]);
|
||||
|
||||
const addEventListener = useCallback(
|
||||
<T extends SSEEvent["kind"]>(eventType: T, listener: EventListener<T>) => {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { readdir } from "node:fs/promises";
|
||||
import { homedir } from "node:os";
|
||||
import { resolve } from "node:path";
|
||||
import { zValidator } from "@hono/zod-validator";
|
||||
import { setCookie } from "hono/cookie";
|
||||
@@ -18,6 +17,7 @@ import { getBranches } from "../service/git/getBranches";
|
||||
import { getCommits } from "../service/git/getCommits";
|
||||
import { getDiff } from "../service/git/getDiff";
|
||||
import { getMcpList } from "../service/mcp/getMcpList";
|
||||
import { claudeCommandsDirPath } from "../service/paths";
|
||||
import { getProject } from "../service/project/getProject";
|
||||
import { getProjects } from "../service/project/getProjects";
|
||||
import { getSession } from "../service/session/getSession";
|
||||
@@ -194,7 +194,7 @@ export const routes = (app: HonoAppType) => {
|
||||
const { project } = await getProject(projectId);
|
||||
|
||||
const [globalCommands, projectCommands] = await Promise.allSettled([
|
||||
readdir(resolve(homedir(), ".claude", "commands"), {
|
||||
readdir(claudeCommandsDirPath, {
|
||||
withFileTypes: true,
|
||||
}).then((dirents) =>
|
||||
dirents
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { type FSWatcher, watch } from "node:fs";
|
||||
import z from "zod";
|
||||
import { claudeProjectPath } from "../paths";
|
||||
import { claudeProjectsDirPath } from "../paths";
|
||||
import { getEventBus, type IEventBus } from "./EventBus";
|
||||
|
||||
const fileRegExp = /(?<projectId>.*?)\/(?<sessionId>.*?)\.jsonl/;
|
||||
@@ -24,10 +24,10 @@ export class FileWatcherService {
|
||||
this.isWatching = true;
|
||||
|
||||
try {
|
||||
console.log("Starting file watcher on:", claudeProjectPath);
|
||||
console.log("Starting file watcher on:", claudeProjectsDirPath);
|
||||
// メインプロジェクトディレクトリを監視
|
||||
this.watcher = watch(
|
||||
claudeProjectPath,
|
||||
claudeProjectsDirPath,
|
||||
{ persistent: false, recursive: true },
|
||||
(eventType, filename) => {
|
||||
if (!filename) return;
|
||||
|
||||
@@ -1,4 +1,20 @@
|
||||
import { homedir } from "node:os";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
export const claudeProjectPath = resolve(homedir(), ".claude", "projects");
|
||||
// biome-ignore lint/complexity/useLiteralKeys: typescript restriction
|
||||
const GLOBAL_CLAUDE_DIR = process.env["GLOBAL_CLAUDE_DIR"];
|
||||
|
||||
export const globalClaudeDirectoryPath =
|
||||
GLOBAL_CLAUDE_DIR === undefined
|
||||
? resolve(homedir(), ".claude")
|
||||
: resolve(GLOBAL_CLAUDE_DIR);
|
||||
|
||||
export const claudeProjectsDirPath = resolve(
|
||||
globalClaudeDirectoryPath,
|
||||
"projects",
|
||||
);
|
||||
|
||||
export const claudeCommandsDirPath = resolve(
|
||||
globalClaudeDirectoryPath,
|
||||
"commands",
|
||||
);
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { constants } from "node:fs";
|
||||
import { access, readdir } from "node:fs/promises";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import { claudeProjectPath } from "../paths";
|
||||
import { claudeProjectsDirPath } from "../paths";
|
||||
import type { Project } from "../types";
|
||||
import { getProjectMeta } from "./getProjectMeta";
|
||||
import { encodeProjectId } from "./id";
|
||||
@@ -10,20 +9,24 @@ import { encodeProjectId } from "./id";
|
||||
export const getProjects = async (): Promise<{ projects: Project[] }> => {
|
||||
try {
|
||||
// Check if the claude projects directory exists
|
||||
await access(claudeProjectPath, constants.F_OK);
|
||||
await access(claudeProjectsDirPath, constants.F_OK);
|
||||
} catch (_error) {
|
||||
// Directory doesn't exist, return empty array
|
||||
console.warn(`Claude projects directory not found at ${claudeProjectPath}`);
|
||||
console.warn(
|
||||
`Claude projects directory not found at ${claudeProjectsDirPath}`,
|
||||
);
|
||||
return { projects: [] };
|
||||
}
|
||||
|
||||
try {
|
||||
const dirents = await readdir(claudeProjectPath, { withFileTypes: true });
|
||||
const dirents = await readdir(claudeProjectsDirPath, {
|
||||
withFileTypes: true,
|
||||
});
|
||||
const projects = await Promise.all(
|
||||
dirents
|
||||
.filter((d) => d.isDirectory())
|
||||
.map(async (d) => {
|
||||
const fullPath = resolve(claudeProjectPath, d.name);
|
||||
const fullPath = resolve(claudeProjectsDirPath, d.name);
|
||||
const id = encodeProjectId(fullPath);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user