Merge pull request #19 from d-kimuson/chore/vrt-refine
Chore/vrt refine
@@ -2,4 +2,16 @@ import { defineCapture } from "../utils/defineCapture";
|
||||
|
||||
export const projectsCapture = defineCapture({
|
||||
href: "/projects",
|
||||
cases: [
|
||||
{
|
||||
name: "new-project-modal",
|
||||
setup: async (page) => {
|
||||
const newProjectButton = page.locator(
|
||||
'[data-testid="new-project-button"]',
|
||||
);
|
||||
await newProjectButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -12,14 +12,14 @@ export const sessionDetailCapture = defineCapture({
|
||||
);
|
||||
if (await menuButton.isVisible()) {
|
||||
await menuButton.click();
|
||||
await page.waitForTimeout(300);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const sessionsTabButton = page.locator(
|
||||
'[data-testid="sessions-tab-button-mobile"]',
|
||||
);
|
||||
if (await sessionsTabButton.isVisible()) {
|
||||
await sessionsTabButton.click();
|
||||
await page.waitForTimeout(300);
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
} else {
|
||||
const sessionsTabButton = page.locator(
|
||||
@@ -27,7 +27,7 @@ export const sessionDetailCapture = defineCapture({
|
||||
);
|
||||
if (await sessionsTabButton.isVisible()) {
|
||||
await sessionsTabButton.click();
|
||||
await page.waitForTimeout(300);
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -41,14 +41,14 @@ export const sessionDetailCapture = defineCapture({
|
||||
);
|
||||
if (await menuButton.isVisible()) {
|
||||
await menuButton.click();
|
||||
await page.waitForTimeout(300);
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const settingsTabButton = page.locator(
|
||||
'[data-testid="settings-tab-button-mobile"]',
|
||||
);
|
||||
if (await settingsTabButton.isVisible()) {
|
||||
await settingsTabButton.click();
|
||||
await page.waitForTimeout(300);
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
} else {
|
||||
const settingsTabButton = page.locator(
|
||||
@@ -56,10 +56,52 @@ export const sessionDetailCapture = defineCapture({
|
||||
);
|
||||
if (await settingsTabButton.isVisible()) {
|
||||
await settingsTabButton.click();
|
||||
await page.waitForTimeout(300);
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "start-new-chat",
|
||||
setup: async (page) => {
|
||||
const menuButton = page.locator(
|
||||
'[data-testid="mobile-sidebar-toggle-button"]',
|
||||
);
|
||||
if (await menuButton.isVisible()) {
|
||||
await menuButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const startNewChatButton = page.locator(
|
||||
'[data-testid="start-new-chat-button-mobile"]',
|
||||
);
|
||||
await startNewChatButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
} else {
|
||||
const startNewChatButton = page.locator(
|
||||
'[data-testid="start-new-chat-button"]',
|
||||
);
|
||||
await startNewChatButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "sidechain-task-modal",
|
||||
setup: async (page) => {
|
||||
const sidechainTaskButton = page
|
||||
.locator('[data-testid="sidechain-task-button"]')
|
||||
.first();
|
||||
if (await sidechainTaskButton.isVisible()) {
|
||||
await sidechainTaskButton.click();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
// モーダルが開いたことを確認
|
||||
const modal = page.locator('[data-testid="sidechain-task-modal"]');
|
||||
await modal.waitFor({ state: "visible", timeout: 3000 });
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
BIN
e2e/snapshots/desktop-dark.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
e2e/snapshots/desktop-light.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 24 KiB |
BIN
e2e/snapshots/mobile-dark.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
e2e/snapshots/mobile-light.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 67 KiB |
BIN
e2e/snapshots/non-existent-page/desktop-dark.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
e2e/snapshots/non-existent-page/mobile-dark.png
Normal file
|
After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
BIN
e2e/snapshots/non-existent-page/tablet-dark.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 371 KiB |
|
After Width: | Height: | Size: 285 KiB |
|
Before Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
Before Width: | Height: | Size: 180 KiB After Width: | Height: | Size: 180 KiB |
|
Before Width: | Height: | Size: 180 KiB |
|
After Width: | Height: | Size: 287 KiB |
|
After Width: | Height: | Size: 288 KiB |
|
Before Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
After Width: | Height: | Size: 312 KiB |
|
After Width: | Height: | Size: 91 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 215 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 284 KiB |
BIN
e2e/snapshots/projects/desktop-dark.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
e2e/snapshots/projects/desktop-light.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 24 KiB |
BIN
e2e/snapshots/projects/mobile-dark.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
e2e/snapshots/projects/mobile-light.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 67 KiB |
BIN
e2e/snapshots/projects/new-project-modal/desktop-dark.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
e2e/snapshots/projects/new-project-modal/desktop-light.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
e2e/snapshots/projects/new-project-modal/mobile-dark.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
e2e/snapshots/projects/new-project-modal/mobile-light.png
Normal file
|
After Width: | Height: | Size: 100 KiB |
BIN
e2e/snapshots/projects/new-project-modal/tablet-dark.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
e2e/snapshots/projects/new-project-modal/tablet-light.png
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
e2e/snapshots/projects/tablet-dark.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
e2e/snapshots/projects/tablet-light.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 55 KiB |
BIN
e2e/snapshots/tablet-dark.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
e2e/snapshots/tablet-light.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 55 KiB |
@@ -20,61 +20,74 @@ export const defineCapture = (options: {
|
||||
.map((path) => path.trim())
|
||||
.filter((path) => path !== "");
|
||||
|
||||
const colorSchemes = ["light", "dark"] as const;
|
||||
|
||||
const captureWithCase = async (
|
||||
device: (typeof testDevices)[number],
|
||||
colorScheme: (typeof colorSchemes)[number],
|
||||
testCase?: CaptureCase,
|
||||
) => {
|
||||
await withPlaywright(
|
||||
async ({ context, cleanUp }) => {
|
||||
const page = await context.newPage();
|
||||
await page.goto(href);
|
||||
try {
|
||||
const page = await context.newPage();
|
||||
await page.goto(href);
|
||||
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
await page.waitForTimeout(1000);
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
if (testCase) {
|
||||
await testCase.setup(page);
|
||||
if (testCase) {
|
||||
await testCase.setup(page);
|
||||
}
|
||||
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
const picturePath = testCase
|
||||
? resolve(
|
||||
"e2e",
|
||||
"snapshots",
|
||||
...paths,
|
||||
testCase.name,
|
||||
`${device.name}-${colorScheme}.png`,
|
||||
)
|
||||
: resolve(
|
||||
"e2e",
|
||||
"snapshots",
|
||||
...paths,
|
||||
`${device.name}-${colorScheme}.png`,
|
||||
);
|
||||
|
||||
await page.screenshot({
|
||||
path: picturePath,
|
||||
fullPage: true,
|
||||
});
|
||||
|
||||
console.log(`[captured] ${picturePath}`);
|
||||
} finally {
|
||||
await cleanUp();
|
||||
}
|
||||
|
||||
const picturePath = testCase
|
||||
? resolve(
|
||||
"e2e",
|
||||
"snapshots",
|
||||
...paths,
|
||||
testCase.name,
|
||||
`${device.name}.png`,
|
||||
)
|
||||
: resolve("e2e", "snapshots", ...paths, `${device.name}.png`);
|
||||
|
||||
await page.screenshot({
|
||||
path: picturePath,
|
||||
fullPage: true,
|
||||
});
|
||||
|
||||
console.log(`[captured] ${picturePath}`);
|
||||
|
||||
await cleanUp();
|
||||
},
|
||||
{
|
||||
contextOptions: {
|
||||
...device.device,
|
||||
baseURL: "http://localhost:4000",
|
||||
colorScheme,
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const tasks = testDevices.flatMap((device): Task[] => {
|
||||
return [
|
||||
return colorSchemes.flatMap((colorScheme): Task[] => [
|
||||
{
|
||||
key: `${device.name}-default`,
|
||||
execute: () => captureWithCase(device),
|
||||
key: `${device.name}-${colorScheme}-default`,
|
||||
execute: () => captureWithCase(device, colorScheme),
|
||||
},
|
||||
...cases.map((testCase) => ({
|
||||
key: `${device.name}-${testCase.name}`,
|
||||
execute: () => captureWithCase(device, testCase),
|
||||
key: `${device.name}-${colorScheme}-${testCase.name}`,
|
||||
execute: () => captureWithCase(device, colorScheme, testCase),
|
||||
})),
|
||||
];
|
||||
]);
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { existsSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import {
|
||||
type Browser,
|
||||
type BrowserContext,
|
||||
@@ -10,8 +8,6 @@ import {
|
||||
} from "playwright";
|
||||
import prexit from "prexit";
|
||||
|
||||
const STORAGE_PATH = path.join(process.cwd(), ".user-data", "session.json");
|
||||
|
||||
type PlaywrightContext = {
|
||||
context: BrowserContext;
|
||||
cleanUp: () => Promise<void>;
|
||||
@@ -35,7 +31,6 @@ const useBrowser = (options: BrowserOptions) => {
|
||||
...launchOptions,
|
||||
});
|
||||
context ??= await browser.newContext({
|
||||
storageState: existsSync(STORAGE_PATH) ? STORAGE_PATH : undefined,
|
||||
...contextOptions,
|
||||
});
|
||||
|
||||
@@ -63,9 +58,6 @@ export const withPlaywright = async <T>(
|
||||
})();
|
||||
let isClosed = false;
|
||||
const cleanUp = async () => {
|
||||
await context.storageState({
|
||||
path: STORAGE_PATH,
|
||||
});
|
||||
await Promise.all(context.pages().map((page) => page.close()));
|
||||
await context.close();
|
||||
await browser.close();
|
||||
|
||||
@@ -35,7 +35,9 @@
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "vitest --run",
|
||||
"test:watch": "vitest",
|
||||
"e2e": "./scripts/e2e.sh"
|
||||
"e2e": "./scripts/e2e/exec_e2e.sh",
|
||||
"e2e:start-server": "./scripts/e2e/start_server.sh",
|
||||
"e2e:capture-snapshots": "./scripts/e2e/capture_snapshots.sh"
|
||||
},
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-code": "^1.0.98",
|
||||
|
||||
7
scripts/e2e/capture_snapshots.sh
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export GLOBAL_CLAUDE_DIR=$(git rev-parse --show-toplevel)/mock-global-claude-dir
|
||||
|
||||
pnpx tsx ./e2e/captureSnapshot/index.ts
|
||||
@@ -2,12 +2,6 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export PORT=4000
|
||||
export GLOBAL_CLAUDE_DIR=$(git rev-parse --show-toplevel)/mock-global-claude-dir
|
||||
|
||||
echo "Check directory structure in $GLOBAL_CLAUDE_DIR:"
|
||||
ls -l $GLOBAL_CLAUDE_DIR
|
||||
|
||||
kill_process_group() {
|
||||
local pid=$1
|
||||
local sig=${2:-TERM}
|
||||
@@ -30,10 +24,10 @@ cleanup() {
|
||||
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
pnpm start & SERVER_PID=$!
|
||||
./scripts/e2e/start_server.sh & SERVER_PID=$!
|
||||
echo "Server started. pid=$SERVER_PID"
|
||||
|
||||
sleep 5 # 即時起動するが、一応少し待っておく
|
||||
|
||||
pnpx tsx ./e2e/captureSnapshot/index.ts
|
||||
./scripts/e2e/capture_snapshots.sh
|
||||
echo "Completed capturing screenshots. Killing server..."
|
||||
11
scripts/e2e/start_server.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
export PORT=4000
|
||||
export GLOBAL_CLAUDE_DIR=$(git rev-parse --show-toplevel)/mock-global-claude-dir
|
||||
|
||||
echo "Check directory structure in $GLOBAL_CLAUDE_DIR:"
|
||||
ls -l $GLOBAL_CLAUDE_DIR
|
||||
|
||||
node ./dist/index.js
|
||||
@@ -1,6 +1,5 @@
|
||||
import { MessageSquareIcon, PlusIcon } from "lucide-react";
|
||||
import { MessageSquareIcon } from "lucide-react";
|
||||
import { type FC, type ReactNode, useState } from "react";
|
||||
import { Button } from "../../../../../components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
@@ -12,7 +11,7 @@ import { NewChat } from "./NewChat";
|
||||
|
||||
export const NewChatModal: FC<{
|
||||
projectId: string;
|
||||
trigger?: ReactNode;
|
||||
trigger: ReactNode;
|
||||
}> = ({ projectId, trigger }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@@ -22,15 +21,11 @@ export const NewChatModal: FC<{
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
{trigger ?? (
|
||||
<Button className="gap-2">
|
||||
<PlusIcon className="w-4 h-4" />
|
||||
New Chat
|
||||
</Button>
|
||||
)}
|
||||
</DialogTrigger>
|
||||
<DialogContent className="w-[95vw] md:w-[80vw]">
|
||||
<DialogTrigger asChild>{trigger}</DialogTrigger>
|
||||
<DialogContent
|
||||
className="w-[95vw] md:w-[80vw]"
|
||||
data-testid="start-new-chat-modal"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<MessageSquareIcon className="w-5 h-5" />
|
||||
|
||||
@@ -204,6 +204,7 @@ export const SessionPageContent: FC<{
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
className="flex-1 overflow-y-auto min-h-0 min-w-0"
|
||||
data-testid="scrollable-content"
|
||||
>
|
||||
<main className="w-full px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 relative z-5 min-w-0">
|
||||
<ConversationList
|
||||
|
||||
@@ -67,6 +67,7 @@ export const SidechainConversationModal: FC<
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="w-full mb-3 items-center justify-start"
|
||||
data-testid="sidechain-task-button"
|
||||
>
|
||||
<div className="flex items-center gap-2 overflow-hidden">
|
||||
<Eye className="h-4 w-4 flex-shrink-0" />
|
||||
@@ -76,7 +77,10 @@ export const SidechainConversationModal: FC<
|
||||
</div>
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="w-[95vw] md:w-[90vw] max-h-[80vh] overflow-hidden flex flex-col px-2 md:px-8">
|
||||
<DialogContent
|
||||
className="w-[95vw] md:w-[90vw] max-h-[80vh] overflow-hidden flex flex-col px-2 md:px-8"
|
||||
data-testid="sidechain-task-modal"
|
||||
>
|
||||
<DialogHeader>
|
||||
<DialogTitle>
|
||||
{title.length > 100 ? `${title.slice(0, 100)}...` : title}
|
||||
|
||||
@@ -98,6 +98,7 @@ export const MobileSidebar: FC<MobileSidebarProps> = ({
|
||||
hasNextPage={hasNextPage}
|
||||
isFetchingNextPage={isFetchingNextPage}
|
||||
onLoadMore={() => fetchNextPage()}
|
||||
isMobile={true}
|
||||
/>
|
||||
);
|
||||
case "mcp":
|
||||
|
||||
@@ -19,6 +19,7 @@ export const SessionsTab: FC<{
|
||||
hasNextPage?: boolean;
|
||||
isFetchingNextPage?: boolean;
|
||||
onLoadMore?: () => void;
|
||||
isMobile?: boolean;
|
||||
}> = ({
|
||||
sessions,
|
||||
currentSessionId,
|
||||
@@ -26,6 +27,7 @@ export const SessionsTab: FC<{
|
||||
hasNextPage,
|
||||
isFetchingNextPage,
|
||||
onLoadMore,
|
||||
isMobile = false,
|
||||
}) => {
|
||||
const sessionProcesses = useAtomValue(sessionProcessesAtom);
|
||||
|
||||
@@ -70,7 +72,16 @@ export const SessionsTab: FC<{
|
||||
<NewChatModal
|
||||
projectId={projectId}
|
||||
trigger={
|
||||
<Button size="sm" variant="outline" className="gap-1.5">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="gap-1.5"
|
||||
data-testid={
|
||||
isMobile === true
|
||||
? "start-new-chat-button-mobile"
|
||||
: "start-new-chat-button"
|
||||
}
|
||||
>
|
||||
<PlusIcon className="w-3.5 h-3.5" />
|
||||
New
|
||||
</Button>
|
||||
|
||||
@@ -52,12 +52,12 @@ export const CreateProjectDialog: FC = () => {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button>
|
||||
<Button data-testid="new-project-button">
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
New Project
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="max-w-2xl">
|
||||
<DialogContent className="max-w-2xl" data-testid="new-project-modal">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Create New Project</DialogTitle>
|
||||
<DialogDescription>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Context, Effect, Layer } from "effect";
|
||||
import { Context, Effect, Either, Layer } from "effect";
|
||||
import type { ControllerResponse } from "../../../lib/effect/toEffectResponse";
|
||||
import type { InferEffect } from "../../../lib/effect/types";
|
||||
import { ProjectRepository } from "../../project/infrastructure/ProjectRepository";
|
||||
@@ -23,9 +23,19 @@ const LayerImpl = Effect.gen(function* () {
|
||||
}
|
||||
|
||||
const projectPath = project.meta.projectPath;
|
||||
const branches = yield* gitService.getBranches(projectPath);
|
||||
const branches = yield* Effect.either(
|
||||
gitService.getBranches(projectPath),
|
||||
);
|
||||
|
||||
if (Either.isLeft(branches)) {
|
||||
return {
|
||||
response: [],
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
}
|
||||
|
||||
return {
|
||||
response: branches,
|
||||
response: branches.right,
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
});
|
||||
@@ -45,9 +55,17 @@ const LayerImpl = Effect.gen(function* () {
|
||||
|
||||
const projectPath = project.meta.projectPath;
|
||||
|
||||
const commits = yield* gitService.getCommits(projectPath);
|
||||
const commits = yield* Effect.either(gitService.getCommits(projectPath));
|
||||
|
||||
if (Either.isLeft(commits)) {
|
||||
return {
|
||||
response: [],
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
}
|
||||
|
||||
return {
|
||||
response: commits,
|
||||
response: commits.right,
|
||||
status: 200,
|
||||
} as const satisfies ControllerResponse;
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ const LayerImpl = Effect.gen(function* () {
|
||||
const envService = yield* EnvService;
|
||||
|
||||
const globalClaudeDirectoryPath = yield* envService
|
||||
.getEnv("CLAUDE_CODE_VIEWER_CC_EXECUTABLE_PATH")
|
||||
.getEnv("GLOBAL_CLAUDE_DIR")
|
||||
.pipe(
|
||||
Effect.map((envVar) =>
|
||||
envVar === undefined
|
||||
|
||||