sync: update from private repo (fd190dc)
Some checks failed
CI / build-and-test (push) Has been cancelled

This commit is contained in:
oss-sync 2026-06-06 00:39:26 +00:00
parent afae52873b
commit caa0d03900
70 changed files with 294 additions and 208 deletions

View File

@ -129,6 +129,36 @@ describe('piece-runner review feedback flow', () => {
expect(instructions[4]).toContain('review 2: add conclusion');
});
it('defaults ownerId and userId to "local" for owner-less (no-auth) jobs', async () => {
workspacePath = makeWorkspace();
let capturedCtx: { ownerId?: unknown; userId?: unknown } | undefined;
executeMovementMock.mockImplementation(async (_movement, _instruction, _client, ctx) => {
capturedCtx = ctx as typeof capturedCtx;
return { next: 'COMPLETE', output: 'done', toolsUsed: [] };
});
// options omit ownerId/userId entirely — a no-auth job has no owner.
// (runPiece args: piece, instruction, client, workspace, callbacks, toolsConfig, options)
await runPiece(makePiece(), 'TASK', {} as never, workspacePath, undefined, undefined, {});
expect(capturedCtx?.ownerId).toBe('local');
expect(capturedCtx?.userId).toBe('local');
});
it('preserves a real ownerId/userId when the job is owned (auth mode)', async () => {
workspacePath = makeWorkspace();
let capturedCtx: { ownerId?: unknown; userId?: unknown } | undefined;
executeMovementMock.mockImplementation(async (_movement, _instruction, _client, ctx) => {
capturedCtx = ctx as typeof capturedCtx;
return { next: 'COMPLETE', output: 'done', toolsUsed: [] };
});
await runPiece(makePiece(), 'TASK', {} as never, workspacePath, undefined, undefined, { ownerId: 'alice', userId: 'alice' });
expect(capturedCtx?.ownerId).toBe('alice');
expect(capturedCtx?.userId).toBe('alice');
});
it('appends safe git status and diff context after verify loops', async () => {
workspacePath = makeGitWorkspace();
const instructions: string[] = [];

View File

@ -725,12 +725,16 @@ function prepareMovementContext(
spawnSubTask: options?.spawnSubTask,
missionBrief: options?.missionBrief,
taskId: options?.taskId,
userId: options?.userId,
// No-auth jobs have no owner (ownerId/userId null). Resolve a single 'local'
// identity — matching where pieces already resolve (worker.ts: ownerId ?? 'local')
// — so MCP (global servers via ctx.ownerId) and the user folder / memory / notes
// (ctx.userId) work in single-tenant deployments instead of being disabled.
userId: options?.userId ?? 'local',
browserSessionState: options?.browserSessionState,
browserSessionProfileId: options?.browserSessionProfileId,
browserSessionProfile: options?.browserSessionProfile,
onAuthExpired: options?.onAuthExpired,
ownerId: options?.ownerId,
ownerId: options?.ownerId ?? 'local',
jobId: options?.jobId,
mcpConfig: options?.mcpConfig,
mcpQuotaState: { files: 0, bytes: 0 },

View File

@ -390,8 +390,8 @@ function AppInner({ isAdmin, authEnabled, user }: { isAdmin: boolean; authEnable
{toast && (
<div className={
toast.variant === 'error'
? 'mx-4 mt-2 px-4 py-2.5 bg-red-50 border border-red-200 rounded-xl text-[13px] text-red-800'
: 'mx-4 mt-2 px-4 py-2.5 bg-green-50 border border-green-200 rounded-xl text-[13px] text-green-800'
? 'mx-4 mt-2 px-4 py-2.5 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 rounded-xl text-[13px] text-red-800 dark:text-red-300'
: 'mx-4 mt-2 px-4 py-2.5 bg-green-50 dark:bg-green-500/15 border border-green-200 dark:border-green-500/30 rounded-xl text-[13px] text-green-800 dark:text-green-300'
}>
{toast.message}
</div>

View File

@ -10,13 +10,13 @@ import { MarkdownText } from '../../lib/markdown-text';
const MD_KINDS = new Set<string>(['preview', 'final', 'ask', 'other']);
const KIND_COLORS: Record<string, { dot: string; badge: string; badgeText: string; border: string }> = {
movement_start: { dot: 'bg-blue-600', badge: 'bg-blue-100', badgeText: 'text-blue-700', border: 'border-slate-200' },
movement_complete:{ dot: 'bg-blue-600', badge: 'bg-blue-100', badgeText: 'text-blue-700', border: 'border-slate-200' },
tool: { dot: 'bg-teal-600', badge: 'bg-teal-100', badgeText: 'text-teal-700', border: 'border-teal-200' },
preview: { dot: 'bg-blue-600', badge: 'bg-blue-100', badgeText: 'text-blue-700', border: 'border-blue-200' },
final: { dot: 'bg-green-600', badge: 'bg-green-100', badgeText: 'text-green-700', border: 'border-green-200' },
ask: { dot: 'bg-amber-500', badge: 'bg-amber-100', badgeText: 'text-amber-700', border: 'border-amber-200' },
preflight: { dot: 'bg-purple-400', badge: 'bg-purple-50', badgeText: 'text-purple-600', border: 'border-purple-100' },
movement_start: { dot: 'bg-blue-600', badge: 'bg-blue-100 dark:bg-blue-500/15', badgeText: 'text-blue-700 dark:text-blue-300', border: 'border-slate-200' },
movement_complete:{ dot: 'bg-blue-600', badge: 'bg-blue-100 dark:bg-blue-500/15', badgeText: 'text-blue-700 dark:text-blue-300', border: 'border-slate-200' },
tool: { dot: 'bg-teal-600', badge: 'bg-teal-100 dark:bg-teal-500/15', badgeText: 'text-teal-700 dark:text-teal-300', border: 'border-teal-200 dark:border-teal-500/30' },
preview: { dot: 'bg-blue-600', badge: 'bg-blue-100 dark:bg-blue-500/15', badgeText: 'text-blue-700 dark:text-blue-300', border: 'border-blue-200 dark:border-blue-500/30' },
final: { dot: 'bg-green-600', badge: 'bg-green-100 dark:bg-green-500/15', badgeText: 'text-green-700 dark:text-green-300', border: 'border-green-200 dark:border-green-500/30' },
ask: { dot: 'bg-amber-500', badge: 'bg-amber-100 dark:bg-amber-500/15', badgeText: 'text-amber-700 dark:text-amber-300', border: 'border-amber-200 dark:border-amber-500/30' },
preflight: { dot: 'bg-purple-400', badge: 'bg-purple-50 dark:bg-purple-500/15', badgeText: 'text-purple-600 dark:text-purple-300', border: 'border-purple-100 dark:border-purple-500/30' },
other: { dot: 'bg-slate-400', badge: 'bg-slate-100', badgeText: 'text-slate-600', border: 'border-slate-200' },
};

View File

@ -96,8 +96,8 @@ export function BrowserSessionPanel() {
<div className="flex items-center justify-between mb-2">
<span className="font-mono text-xs">{session.id.slice(0, 8)}...</span>
<span className={`px-2 py-0.5 rounded text-xs ${
session.state === 'user_interactive' ? 'bg-yellow-100 text-yellow-700' :
session.state === 'agent_controlled' ? 'bg-blue-100 text-blue-700' :
session.state === 'user_interactive' ? 'bg-yellow-100 dark:bg-yellow-500/15 text-yellow-700 dark:text-yellow-300' :
session.state === 'agent_controlled' ? 'bg-blue-100 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300' :
'bg-gray-100 text-gray-700'
}`}>
{session.state}
@ -121,7 +121,7 @@ export function BrowserSessionPanel() {
)}
<button
onClick={() => deleteMutation.mutate(session.id)}
className="px-2 py-1 text-xs bg-red-100 text-red-700 rounded hover:bg-red-200"
className="px-2 py-1 text-xs bg-red-100 dark:bg-red-500/15 text-red-700 dark:text-red-300 rounded hover:bg-red-200 dark:hover:bg-red-500/25"
>
Destroy
</button>

View File

@ -179,12 +179,12 @@ function ChecklistCard({ comment }: { comment: LocalTaskComment }) {
{/* Summary badges */}
<div className="flex gap-1.5 mt-2 text-2xs">
{summary.done > 0 && (
<span className="inline-flex items-center gap-1 bg-emerald-50 text-emerald-700 border border-emerald-100 px-1.5 py-0.5 rounded font-mono tabular-nums">
<span className="inline-flex items-center gap-1 bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 border border-emerald-100 dark:border-emerald-500/30 px-1.5 py-0.5 rounded font-mono tabular-nums">
<StatusIcon status="done" />{summary.done}
</span>
)}
{summary.failed > 0 && (
<span className="inline-flex items-center gap-1 bg-red-50 text-red-700 border border-red-100 px-1.5 py-0.5 rounded font-mono tabular-nums">
<span className="inline-flex items-center gap-1 bg-red-50 dark:bg-red-500/15 text-red-700 dark:text-red-300 border border-red-100 dark:border-red-500/30 px-1.5 py-0.5 rounded font-mono tabular-nums">
<StatusIcon status="failed" />{summary.failed}
</span>
)}
@ -343,7 +343,7 @@ export function ChatMessage({ comment, taskId, imageBaseUrl, isStaleThinking }:
const isPending = !comment.injectedAt;
return (
<div className="flex justify-end">
<div className="max-w-[82%] bg-amber-50 border border-amber-200 text-slate-900 rounded-2xl rounded-br-md px-4 py-3">
<div className="max-w-[82%] bg-amber-50 dark:bg-amber-500/15 border border-amber-200 dark:border-amber-500/30 text-slate-900 rounded-2xl rounded-br-md px-4 py-3">
<div className="text-2xs text-amber-500 mb-1.5">
{author} · {new Date(createdAt).toLocaleString()}
</div>
@ -374,7 +374,7 @@ export function ChatMessage({ comment, taskId, imageBaseUrl, isStaleThinking }:
if (kind === 'ask') {
return (
<div className="flex justify-start">
<div className="max-w-[82%] bg-amber-50 border border-amber-200 text-slate-900 rounded-2xl rounded-bl-md px-4 py-3 shadow-sm">
<div className="max-w-[82%] bg-amber-50 dark:bg-amber-500/15 border border-amber-200 dark:border-amber-500/30 text-slate-900 rounded-2xl rounded-bl-md px-4 py-3 shadow-sm">
<div className="text-2xs text-amber-500 mb-1.5">
{author} · {new Date(createdAt).toLocaleString()}
</div>
@ -388,7 +388,7 @@ export function ChatMessage({ comment, taskId, imageBaseUrl, isStaleThinking }:
if (kind === 'result') {
return (
<div className="flex justify-start">
<div className="w-full bg-green-50 border border-green-200 text-slate-900 rounded-xl px-4 py-3 shadow-sm">
<div className="w-full bg-green-50 dark:bg-green-500/15 border border-green-200 dark:border-green-500/30 text-slate-900 rounded-xl px-4 py-3 shadow-sm">
<div className="text-2xs text-green-500 mb-1.5">
{author} · {new Date(createdAt).toLocaleString()}
</div>

View File

@ -244,14 +244,14 @@ export function ChatPane({ task, comments, onSubmit, onCancel, onOpenDetail }: C
{isBusy && (
<div className={`inline-flex items-center gap-1.5 px-1.5 py-0.5 rounded border ${
isWaitingSubtasks
? 'border-indigo-100 bg-indigo-50'
: 'border-emerald-100 bg-emerald-50'
? 'border-indigo-100 dark:border-indigo-500/30 bg-indigo-50 dark:bg-indigo-500/15'
: 'border-emerald-100 dark:border-emerald-500/30 bg-emerald-50 dark:bg-emerald-500/15'
}`}>
<span className={`w-1.5 h-1.5 rounded-full animate-pulse ${
isWaitingSubtasks ? 'bg-indigo-500' : 'bg-emerald-500'
}`} />
<span className={`text-[10px] font-medium ${
isWaitingSubtasks ? 'text-indigo-700' : 'text-emerald-700'
isWaitingSubtasks ? 'text-indigo-700 dark:text-indigo-300' : 'text-emerald-700 dark:text-emerald-300'
}`}>
{isWaitingSubtasks ? 'subtasks' : 'running'}
</span>
@ -385,8 +385,8 @@ export function ChatPane({ task, comments, onSubmit, onCancel, onOpenDetail }: C
{isBusy && (
<div className={`flex items-center gap-2 mb-2 px-2.5 py-1 rounded-md text-2xs ${
canInterject
? 'bg-amber-50 border border-amber-100 text-amber-700'
: 'bg-blue-50 border border-blue-100 text-blue-700'
? 'bg-amber-50 dark:bg-amber-500/15 border border-amber-100 dark:border-amber-500/30 text-amber-700 dark:text-amber-300'
: 'bg-blue-50 dark:bg-blue-500/15 border border-blue-100 dark:border-blue-500/30 text-blue-700 dark:text-blue-300'
}`}>
<svg className="w-3 h-3 animate-spin flex-shrink-0" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
@ -396,13 +396,13 @@ export function ChatPane({ task, comments, onSubmit, onCancel, onOpenDetail }: C
</div>
)}
{sendError && !isBusy && (
<div className="flex items-center justify-between gap-2 mb-2 px-2.5 py-1 bg-red-50 border border-red-100 rounded-md text-2xs text-red-700">
<div className="flex items-center justify-between gap-2 mb-2 px-2.5 py-1 bg-red-50 dark:bg-red-500/15 border border-red-100 dark:border-red-500/30 rounded-md text-2xs text-red-700 dark:text-red-300">
<span className="truncate"> {sendError}</span>
<button
type="button"
onClick={() => void handleSubmit()}
disabled={submitting}
className="flex-shrink-0 px-2 h-6 bg-canvas border border-red-200 rounded text-[10px] font-medium text-red-700 hover:bg-red-100 disabled:opacity-50"
className="flex-shrink-0 px-2 h-6 bg-canvas border border-red-200 rounded text-[10px] font-medium text-red-700 dark:text-red-300 hover:bg-red-100 dark:hover:bg-red-500/15 disabled:opacity-50"
>
</button>
@ -461,7 +461,7 @@ export function ChatPane({ task, comments, onSubmit, onCancel, onOpenDetail }: C
<button
disabled={cancelling}
onClick={() => void handleCancel()}
className="px-3 h-9 bg-canvas border border-red-200 text-red-700 rounded-md text-xs font-semibold disabled:opacity-50 hover:bg-red-50 flex-shrink-0 transition-colors"
className="px-3 h-9 bg-canvas border border-red-200 text-red-700 dark:text-red-300 rounded-md text-xs font-semibold disabled:opacity-50 hover:bg-red-50 dark:hover:bg-red-500/15 flex-shrink-0 transition-colors"
title="エージェントの実行を停止"
>
{cancelling ? '停止中...' : '停止'}

View File

@ -92,17 +92,17 @@ export function SubtaskInlineCard({ subtasks, subtaskCount, subtaskCompleted }:
{/* Summary badges */}
<div className="flex gap-1.5 mt-2 text-2xs">
{subtaskCompleted > 0 && (
<span className="inline-flex items-center gap-1 bg-emerald-50 text-emerald-700 border border-emerald-100 px-1.5 py-0.5 rounded font-mono tabular-nums">
<span className="inline-flex items-center gap-1 bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 border border-emerald-100 dark:border-emerald-500/30 px-1.5 py-0.5 rounded font-mono tabular-nums">
<SubtaskStatusIcon status="succeeded" />{subtaskCompleted}
</span>
)}
{running > 0 && (
<span className="inline-flex items-center gap-1 bg-blue-50 text-blue-700 border border-blue-100 px-1.5 py-0.5 rounded font-mono tabular-nums">
<span className="inline-flex items-center gap-1 bg-blue-50 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300 border border-blue-100 dark:border-blue-500/30 px-1.5 py-0.5 rounded font-mono tabular-nums">
<SubtaskStatusIcon status="running" />{running}
</span>
)}
{failed > 0 && (
<span className="inline-flex items-center gap-1 bg-red-50 text-red-700 border border-red-100 px-1.5 py-0.5 rounded font-mono tabular-nums">
<span className="inline-flex items-center gap-1 bg-red-50 dark:bg-red-500/15 text-red-700 dark:text-red-300 border border-red-100 dark:border-red-500/30 px-1.5 py-0.5 rounded font-mono tabular-nums">
<SubtaskStatusIcon status="failed" />{failed}
</span>
)}

View File

@ -110,7 +110,7 @@ function ToolCallRow({ tc }: { tc: ToolCallData }) {
{tc.isError ? '✕' : '✓'}
</span>
{display.server && (
<span className="font-mono text-[10px] text-purple-500 bg-purple-50 px-1 py-px rounded flex-shrink-0">
<span className="font-mono text-[10px] text-purple-500 dark:text-purple-300 bg-purple-50 dark:bg-purple-500/15 px-1 py-px rounded flex-shrink-0">
{display.server}
</span>
)}

View File

@ -200,7 +200,7 @@ export function CreateTaskDialog({ onClose, onSubmit, initialPiece, initialBody,
{/* MCP warnings (always visible when applicable) */}
{missingMcp.length > 0 && (
<div className="p-3 bg-yellow-50 border border-yellow-300 rounded text-xs text-yellow-900 space-y-2">
<div className="p-3 bg-yellow-50 dark:bg-yellow-500/15 border border-yellow-300 dark:border-yellow-500/30 rounded text-xs text-yellow-900 dark:text-yellow-300 space-y-2">
<div>
<strong> MCP :</strong> {missingMcp.join(', ')}
</div>
@ -217,7 +217,7 @@ export function CreateTaskDialog({ onClose, onSubmit, initialPiece, initialBody,
</a>
))}
</div>
<div className="text-2xs text-yellow-700">
<div className="text-2xs text-yellow-700 dark:text-yellow-300">
waiting_human
</div>
</div>

View File

@ -69,7 +69,7 @@ export function MarkdownWidget({ widget, onSave, onDelete }: Props) {
if (!window.confirm(`"${widget.title}" を削除しますか?`)) return;
await onDelete();
}}
className="px-3 py-1 text-xs text-red-600 hover:bg-red-50 rounded"
className="px-3 py-1 text-xs text-red-600 hover:bg-red-50 dark:hover:bg-red-500/15 rounded"
aria-label="削除"
>
🗑

View File

@ -118,7 +118,7 @@ function WorkerRow({
)}
<span className="truncate">{name}</span>
{proxy && (
<span className="px-1 py-0.5 rounded text-[9px] font-medium bg-violet-50 text-violet-700 leading-none">
<span className="px-1 py-0.5 rounded text-[9px] font-medium bg-violet-50 dark:bg-violet-500/15 text-violet-700 dark:text-violet-300 leading-none">
proxy
</span>
)}

View File

@ -150,7 +150,7 @@ export function ContinueWithPieceDialog({
</div>
{submitError && (
<div className="text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1.5">
<div className="text-xs text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 rounded px-2 py-1.5">
{submitError}
</div>
)}

View File

@ -102,7 +102,7 @@ function ShareButton({ taskId, shareToken, onShareChange }: { taskId: number; sh
onClick={handleCopy}
title={copied ? 'コピーしました' : '共有リンクをコピー'}
aria-label="共有リンクをコピー"
className={`${iconBtnBase} ${copied ? 'border-emerald-200 bg-emerald-50 text-emerald-700' : 'border-hairline bg-canvas text-slate-600 hover:text-slate-900 hover:bg-surface'}`}
className={`${iconBtnBase} ${copied ? 'border-emerald-200 dark:border-emerald-500/30 bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300' : 'border-hairline bg-canvas text-slate-600 hover:text-slate-900 hover:bg-surface'}`}
>
{copied ? (
<svg className="w-3.5 h-3.5" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
@ -120,7 +120,7 @@ function ShareButton({ taskId, shareToken, onShareChange }: { taskId: number; sh
disabled={unshareMutation.isPending}
title="共有を停止"
aria-label="共有を停止"
className={`${iconBtnBase} border-hairline bg-canvas text-slate-500 hover:text-red-700 hover:border-red-200 hover:bg-red-50`}
className={`${iconBtnBase} border-hairline bg-canvas text-slate-500 hover:text-red-700 dark:hover:text-red-300 hover:border-red-200 hover:bg-red-50 dark:hover:bg-red-500/15`}
>
{unshareMutation.isPending ? (
<svg className="w-3.5 h-3.5 animate-spin" viewBox="0 0 24 24" fill="none">

View File

@ -279,7 +279,7 @@ export function LocalDetailPanel({
<button
disabled={deleting}
onClick={handleDelete}
className="px-3 h-7 bg-canvas border border-red-200 text-red-700 rounded-md text-xs font-medium disabled:opacity-50 hover:bg-red-50 transition-colors"
className="px-3 h-7 bg-canvas border border-red-200 text-red-700 dark:text-red-300 rounded-md text-xs font-medium disabled:opacity-50 hover:bg-red-50 dark:hover:bg-red-500/15 transition-colors"
>
{deleting ? '削除中...' : '削除'}
</button>

View File

@ -41,7 +41,7 @@ export function ReflectionBadge({ taskId }: ReflectionBadgeProps) {
return (
<a
href={href}
className="inline-flex items-center gap-1 rounded-full bg-amber-50 px-2 py-0.5 text-xs text-amber-700 hover:bg-amber-100"
className="inline-flex items-center gap-1 rounded-full bg-amber-50 dark:bg-amber-500/15 px-2 py-0.5 text-xs text-amber-700 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-500/15"
>
🧠 {label}
</a>

View File

@ -67,7 +67,7 @@ export function BrowserTab({ taskId }: { taskId: number }) {
if (isError) {
const msg = error instanceof Error ? error.message : String(error);
return (
<div className="p-4 text-sm text-red-700">
<div className="p-4 text-sm text-red-700 dark:text-red-300">
: {msg}
</div>
);
@ -77,7 +77,7 @@ export function BrowserTab({ taskId }: { taskId: number }) {
if (data?.reason === 'novnc_not_installed') {
return (
<div className="bg-canvas border border-amber-300 rounded-md p-6 text-sm text-slate-700">
<p className="font-medium text-amber-800 mb-2">noVNC Web (vnc.html) </p>
<p className="font-medium text-amber-800 dark:text-amber-300 mb-2">noVNC Web (vnc.html) </p>
<p className="text-xs leading-relaxed mb-2">
noVNC HTML/JS
<code className="mx-1 px-1 rounded bg-slate-100 font-mono text-2xs">vendor/noVNC/</code>

View File

@ -83,7 +83,7 @@ function FeedbackPanel({ task }: { task: LocalTask }) {
<button
onClick={() => handleRatingClick('good')}
className={`px-3 py-1.5 rounded-lg text-sm border transition-colors ${
rating === 'good' ? 'bg-green-50 border-green-300 text-green-700' : 'border-slate-200 text-slate-500 hover:border-slate-300'
rating === 'good' ? 'bg-green-50 dark:bg-green-500/15 border-green-300 dark:border-green-500/30 text-green-700 dark:text-green-300' : 'border-slate-200 text-slate-500 hover:border-slate-300'
}`}
>
👍
@ -91,7 +91,7 @@ function FeedbackPanel({ task }: { task: LocalTask }) {
<button
onClick={() => handleRatingClick('bad')}
className={`px-3 py-1.5 rounded-lg text-sm border transition-colors ${
rating === 'bad' ? 'bg-red-50 border-red-300 text-red-700' : 'border-slate-200 text-slate-500 hover:border-slate-300'
rating === 'bad' ? 'bg-red-50 dark:bg-red-500/15 border-red-300 dark:border-red-500/30 text-red-700 dark:text-red-300' : 'border-slate-200 text-slate-500 hover:border-slate-300'
}`}
>
👎

View File

@ -59,13 +59,13 @@ function parseEventsJsonl(raw: string): ParseSummary {
// Categorize for filter chips and color coding.
const CATEGORIES: Array<{ id: string; label: string; kinds: string[]; tone: string }> = [
{ id: 'run', label: 'Run', kinds: ['run_start', 'run_complete'], tone: 'bg-surface-2 text-slate-700 border-hairline' },
{ id: 'movement', label: 'Movement', kinds: ['movement_start', 'movement_complete', 'transition', 'complete'], tone: 'bg-blue-50 text-blue-800 border-blue-100' },
{ id: 'movement', label: 'Movement', kinds: ['movement_start', 'movement_complete', 'transition', 'complete'], tone: 'bg-blue-50 text-blue-800 border-blue-100 dark:bg-blue-500/15 dark:text-blue-300 dark:border-blue-500/30' },
{ id: 'tool', label: 'Tool', kinds: ['tool_call', 'tool_result'], tone: 'bg-canvas text-slate-700 border-hairline' },
{ id: 'llm', label: 'LLM', kinds: ['llm_call_start', 'llm_call_end'], tone: 'bg-indigo-50 text-indigo-800 border-indigo-100' },
{ id: 'cache', label: 'Cache', kinds: ['cache_set', 'cache_hit', 'cache_invalidate'], tone: 'bg-amber-50 text-amber-800 border-amber-100' },
{ id: 'memory', label: 'Memory', kinds: ['memory_invalidate', 'memory_update_call', 'memory_handoff_write', 'memory_handoff_read', 'memory_delta_write', 'memory_delta_absorb', 'memory_snapshot_written', 'memory_snapshot_failed'], tone: 'bg-emerald-50 text-emerald-800 border-emerald-100' },
{ id: 'watchdog', label: 'Watchdog', kinds: ['watchdog_fire', 'followup_detected'], tone: 'bg-red-50 text-red-800 border-red-100' },
{ id: 'context', label: 'Context', kinds: ['context_action'], tone: 'bg-violet-50 text-violet-800 border-violet-100' },
{ id: 'llm', label: 'LLM', kinds: ['llm_call_start', 'llm_call_end'], tone: 'bg-indigo-50 text-indigo-800 border-indigo-100 dark:bg-indigo-500/15 dark:text-indigo-300 dark:border-indigo-500/30' },
{ id: 'cache', label: 'Cache', kinds: ['cache_set', 'cache_hit', 'cache_invalidate'], tone: 'bg-amber-50 text-amber-800 border-amber-100 dark:bg-amber-500/15 dark:text-amber-300 dark:border-amber-500/30' },
{ id: 'memory', label: 'Memory', kinds: ['memory_invalidate', 'memory_update_call', 'memory_handoff_write', 'memory_handoff_read', 'memory_delta_write', 'memory_delta_absorb', 'memory_snapshot_written', 'memory_snapshot_failed'], tone: 'bg-emerald-50 text-emerald-800 border-emerald-100 dark:bg-emerald-500/15 dark:text-emerald-300 dark:border-emerald-500/30' },
{ id: 'watchdog', label: 'Watchdog', kinds: ['watchdog_fire', 'followup_detected'], tone: 'bg-red-50 text-red-800 border-red-100 dark:bg-red-500/15 dark:text-red-300 dark:border-red-500/30' },
{ id: 'context', label: 'Context', kinds: ['context_action'], tone: 'bg-violet-50 text-violet-800 border-violet-100 dark:bg-violet-500/15 dark:text-violet-300 dark:border-violet-500/30' },
];
/**
@ -415,7 +415,7 @@ export function TraceTab({ taskId }: TraceTabProps) {
const bar = durationBarStyle(toolTimings.llm.totalMs);
return (
<div className="flex items-center gap-2 text-2xs font-mono">
<span className="min-w-[14ch] text-indigo-700 shrink-0">llm × {toolTimings.llm.count}</span>
<span className="min-w-[14ch] text-indigo-700 dark:text-indigo-300 shrink-0">llm × {toolTimings.llm.count}</span>
<div className="flex-1 min-w-0 h-2 bg-slate-100 rounded relative overflow-hidden">
<div className={`${bar.tone} h-full`} style={{ width: `${bar.widthPct}%` }} />
</div>
@ -429,7 +429,7 @@ export function TraceTab({ taskId }: TraceTabProps) {
const bar = durationBarStyle(totalMs);
return (
<div key={tool} className="flex items-center gap-2 text-2xs font-mono">
<span className={`min-w-[14ch] shrink-0 truncate ${errors > 0 ? 'text-red-700' : 'text-slate-700'}`} title={tool}>
<span className={`min-w-[14ch] shrink-0 truncate ${errors > 0 ? 'text-red-700 dark:text-red-300' : 'text-slate-700'}`} title={tool}>
{tool} × {count}
{errors > 0 ? <span className="text-red-600"> {errors}</span> : null}
</span>

View File

@ -24,7 +24,7 @@ export function ConsoleHeader({ state, status }: { state: ConnState; status: Con
return <div className="px-3 py-2 text-sm text-amber-600">{state.kind === 'connecting' ? 'Connecting…' : 'Restoring scrollback…'}</div>;
}
if (state.kind === 'disconnected') {
return <div className="px-3 py-2 text-sm text-red-700">Disconnected ({state.reason ?? 'unknown'}).</div>;
return <div className="px-3 py-2 text-sm text-red-700 dark:text-red-300">Disconnected ({state.reason ?? 'unknown'}).</div>;
}
const startedAt = status?.started_at ? new Date(status.started_at).getTime() : now;
const lastAt = status?.last_activity_at ? new Date(status.last_activity_at).getTime() : now;

View File

@ -56,7 +56,7 @@ export function AmazonProductsCard({ data, onExpand }: { data: AmazonData; onExp
<div className="text-center mt-2">
<button
onClick={onExpand}
className="text-blue-500 hover:text-blue-700 cursor-pointer bg-transparent border-none"
className="text-blue-500 hover:text-blue-700 dark:hover:text-blue-300 cursor-pointer bg-transparent border-none"
style={{ fontSize: 11 }}
>
&#9660;

View File

@ -32,7 +32,7 @@ export function MapPlacesCard({ data, onExpand }: { data: MapData; onExpand: ()
<div className="text-center mt-2">
<button
onClick={onExpand}
className="text-blue-500 hover:text-blue-700 cursor-pointer bg-transparent border-none"
className="text-blue-500 hover:text-blue-700 dark:hover:text-blue-300 cursor-pointer bg-transparent border-none"
style={{ fontSize: 11 }}
>
&#9660;

View File

@ -55,7 +55,7 @@ export function XPostsCard({ data, onExpand }: { data: XPostData; onExpand: () =
<div className="text-center mt-2">
<button
onClick={onExpand}
className="text-blue-500 hover:text-blue-700 cursor-pointer bg-transparent border-none"
className="text-blue-500 hover:text-blue-700 dark:hover:text-blue-300 cursor-pointer bg-transparent border-none"
style={{ fontSize: 11 }}
>
&#9660;

View File

@ -59,7 +59,7 @@ export function YouTubeVideosCard({ data, onExpand }: { data: YouTubeData; onExp
<div className="text-center mt-2">
<button
onClick={onExpand}
className="text-blue-500 hover:text-blue-700 cursor-pointer bg-transparent border-none"
className="text-blue-500 hover:text-blue-700 dark:hover:text-blue-300 cursor-pointer bg-transparent border-none"
style={{ fontSize: 11 }}
>
&#9660;

View File

@ -517,9 +517,9 @@ ${bodyHtml}
// --- JSONL ---
function badgeClass(color: 'green' | 'orange' | 'red' | 'gray'): string {
if (color === 'green') return 'bg-green-100 text-green-800';
if (color === 'orange') return 'bg-amber-100 text-amber-800';
if (color === 'red') return 'bg-red-100 text-red-800';
if (color === 'green') return 'bg-green-100 text-green-800 dark:bg-green-500/15 dark:text-green-300';
if (color === 'orange') return 'bg-amber-100 text-amber-800 dark:bg-amber-500/15 dark:text-amber-300';
if (color === 'red') return 'bg-red-100 text-red-800 dark:bg-red-500/15 dark:text-red-300';
return 'bg-slate-100 text-slate-600';
}
@ -782,7 +782,7 @@ export function FilePreview({ name, content, imageSrc, markdownImageBaseUrl, onC
</div>
<div className="p-4 overflow-auto flex-1">
{mode === 'view' && error && (
<div className="mb-2 px-3 py-2 bg-red-50 border border-red-200 text-red-700 text-xs rounded">{error}</div>
<div className="mb-2 px-3 py-2 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 text-red-700 dark:text-red-300 text-xs rounded">{error}</div>
)}
{body}
</div>

View File

@ -52,15 +52,15 @@ export const LocalTaskListItem = memo(function LocalTaskListItem({ task, active,
<span className="text-slate-400">system</span>
)}
{task.visibility === 'private' && (
<span className="px-1 rounded text-[10px] font-medium bg-amber-50 text-amber-700 border border-amber-100" title="Private">private</span>
<span className="px-1 rounded text-[10px] font-medium bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-100 dark:border-amber-500/30" title="Private">private</span>
)}
{task.visibility === 'org' && (
<span className="px-1 rounded text-[10px] font-medium bg-blue-50 text-blue-700 border border-blue-100" title={`Shared with ${task.visibilityScopeOrgName ?? 'org'}`}>
<span className="px-1 rounded text-[10px] font-medium bg-blue-50 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300 border border-blue-100 dark:border-blue-500/30" title={`Shared with ${task.visibilityScopeOrgName ?? 'org'}`}>
{task.visibilityScopeOrgName ?? 'org'}
</span>
)}
{task.visibility === 'public' && (
<span className="px-1 rounded text-[10px] font-medium bg-emerald-50 text-emerald-700 border border-emerald-100" title="Public">public</span>
<span className="px-1 rounded text-[10px] font-medium bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 border border-emerald-100 dark:border-emerald-500/30" title="Public">public</span>
)}
</div>
</button>

View File

@ -124,7 +124,7 @@ function AssetUploader({
type="button"
onClick={() => void handleClear()}
disabled={busy}
className="px-2.5 h-7 text-2xs font-medium text-red-700 border border-red-200 bg-canvas hover:bg-red-50 rounded-md disabled:opacity-50 transition-colors"
className="px-2.5 h-7 text-2xs font-medium text-red-700 dark:text-red-300 border border-red-200 bg-canvas hover:bg-red-50 dark:hover:bg-red-500/15 rounded-md disabled:opacity-50 transition-colors"
>
</button>

View File

@ -242,16 +242,16 @@ function ConfigFormInner({ section }: ConfigFormProps) {
<div
className={`sticky bottom-0 px-3 py-2.5 mt-6 border rounded-md flex items-center justify-end gap-2 transition-colors ${
dirty
? 'bg-amber-50 border-amber-300 shadow-[0_2px_8px_rgba(180,83,9,0.08)]'
? 'bg-amber-50 dark:bg-amber-500/15 border-amber-300 dark:border-amber-500/30 shadow-[0_2px_8px_rgba(180,83,9,0.08)]'
: 'bg-canvas border-hairline'
}`}
>
{toast ? (
<span className={`text-2xs mr-auto ${toast.startsWith('エラー') ? 'text-red-600' : 'text-emerald-700'}`}>
<span className={`text-2xs mr-auto ${toast.startsWith('エラー') ? 'text-red-600' : 'text-emerald-700 dark:text-emerald-300'}`}>
{toast}
</span>
) : dirty ? (
<span className="text-xs mr-auto text-amber-800 flex items-center gap-1.5 font-medium min-w-0">
<span className="text-xs mr-auto text-amber-800 dark:text-amber-300 flex items-center gap-1.5 font-medium min-w-0">
<span className="inline-block w-1.5 h-1.5 rounded-full bg-amber-500 animate-pulse flex-shrink-0" aria-hidden />
<span className="truncate">
<span className="hidden sm:inline">: {dirtyCount} Save &amp; Apply</span>

View File

@ -106,9 +106,9 @@ export function GatewayKeyRawKeyDialog({ rawKey, team, reason, onClose }: Props)
</h3>
<p className="text-xs text-slate-500 mb-4">team: {team}</p>
<div className="rounded border border-red-300 bg-red-50 p-3 mb-3">
<p className="text-sm text-red-800 font-medium"> </p>
<p className="text-xs text-red-700 mt-1">
<div className="rounded border border-red-300 dark:border-red-500/30 bg-red-50 dark:bg-red-500/15 p-3 mb-3">
<p className="text-sm text-red-800 dark:text-red-300 font-medium"> </p>
<p className="text-xs text-red-700 dark:text-red-300 mt-1">
LLM
Rotate
</p>

View File

@ -282,9 +282,9 @@ export function GatewayKeysSection({ showToast }: Props) {
</td>
<td className="p-2 text-xs">
{isRevoked ? (
<span className="px-1.5 py-0.5 bg-red-50 text-red-700 rounded">revoked</span>
<span className="px-1.5 py-0.5 bg-red-50 dark:bg-red-500/15 text-red-700 dark:text-red-300 rounded">revoked</span>
) : (
<span className="px-1.5 py-0.5 bg-green-50 text-green-700 rounded">active</span>
<span className="px-1.5 py-0.5 bg-green-50 dark:bg-green-500/15 text-green-700 dark:text-green-300 rounded">active</span>
)}
</td>
<td className="p-2 text-right">
@ -308,7 +308,7 @@ export function GatewayKeysSection({ showToast }: Props) {
type="button"
disabled={isRevoked}
onClick={() => handleRevoke(row)}
className="px-2 py-0.5 text-xs rounded border border-red-300 text-red-700 hover:bg-red-50 disabled:opacity-40 disabled:cursor-not-allowed"
className="px-2 py-0.5 text-xs rounded border border-red-300 text-red-700 dark:text-red-300 hover:bg-red-50 dark:hover:bg-red-500/15 disabled:opacity-40 disabled:cursor-not-allowed"
>
Revoke
</button>

View File

@ -87,21 +87,21 @@ function StatusBadge({ status }: { status: GatewayServerStatus | undefined }) {
}
if (status.state === 'running') {
return (
<span className="text-xs px-2 py-0.5 rounded bg-emerald-50 text-emerald-700 border border-emerald-200">
<span className="text-xs px-2 py-0.5 rounded bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 border border-emerald-200 dark:border-emerald-500/30">
running (mounted at /v1, port {status.sharedPort})
</span>
);
}
if (status.state === 'misconfigured') {
return (
<span className="text-xs px-2 py-0.5 rounded bg-red-50 text-red-700 border border-red-200">
<span className="text-xs px-2 py-0.5 rounded bg-red-50 dark:bg-red-500/15 text-red-700 dark:text-red-300 border border-red-200 dark:border-red-500/30">
misconfigured ({status.errors.length} error{status.errors.length === 1 ? '' : 's'})
</span>
);
}
if (status.state === 'starting' || status.state === 'stopping') {
return (
<span className="text-xs px-2 py-0.5 rounded bg-amber-50 text-amber-700 border border-amber-200">
<span className="text-xs px-2 py-0.5 rounded bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-200 dark:border-amber-500/30">
{status.state}
</span>
);
@ -206,7 +206,7 @@ export function GatewayServerForm({ config, onChange }: SectionFormProps) {
<StatusBadge status={statusQuery.data} />
</div>
{statusQuery.data?.errors && statusQuery.data.errors.length > 0 && (
<ul className="mt-2 text-xs text-red-700 bg-red-50 border border-red-200 rounded p-2 space-y-0.5">
<ul className="mt-2 text-xs text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 rounded p-2 space-y-0.5">
{statusQuery.data.errors.map((e, i) => (
<li key={i}> {e}</li>
))}
@ -260,7 +260,7 @@ export function GatewayServerForm({ config, onChange }: SectionFormProps) {
return (
<div
key={i}
className={`border rounded-md p-3 space-y-2 relative ${errs.length > 0 ? 'border-red-200 bg-red-50/30' : 'border-slate-200'}`}
className={`border rounded-md p-3 space-y-2 relative ${errs.length > 0 ? 'border-red-200 dark:border-red-500/30 bg-red-50/30 dark:bg-red-500/10' : 'border-slate-200'}`}
>
<button
onClick={() => removeBackend(i)}
@ -306,7 +306,7 @@ export function GatewayServerForm({ config, onChange }: SectionFormProps) {
reference into a literal "${VAR}" string and
the env var indirection is lost. */}
{typeof b.apiKey === 'string' && b.apiKey.trimStart().startsWith('${') && (
<p className="text-2xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-2 py-1 mt-1">
<p className="text-2xs text-amber-700 dark:text-amber-300 bg-amber-50 dark:bg-amber-500/15 border border-amber-200 dark:border-amber-500/30 rounded px-2 py-1 mt-1">
env var reference detected: 保存すると <code>{b.apiKey}</code> config.yaml env env config.yaml
</p>
)}

View File

@ -24,7 +24,7 @@ export function KnowledgeNamespacesForm({ config, onChange }: SectionFormProps)
<div className="flex items-center gap-2">
<h2 className="text-base font-semibold text-slate-800">Knowledge (DKS)</h2>
<span
className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold uppercase tracking-wide bg-amber-100 text-amber-800 border border-amber-300"
className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold uppercase tracking-wide bg-amber-100 dark:bg-amber-500/15 text-amber-800 dark:text-amber-300 border border-amber-300 dark:border-amber-500/30"
title="この機能は legacy です。新規の知識検索統合は MCP server 経由を推奨"
>
LEGACY
@ -33,14 +33,14 @@ export function KnowledgeNamespacesForm({ config, onChange }: SectionFormProps)
<div
role="note"
className="rounded border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-900"
className="rounded border border-amber-300 dark:border-amber-500/30 bg-amber-50 dark:bg-amber-500/15 px-3 py-2 text-xs text-amber-900 dark:text-amber-300"
>
DKS <strong>legacy</strong> {' '}
<strong>MCP server </strong> namespace
namespace {' '}
<a
href="/help"
className="underline text-amber-900 hover:text-amber-700"
className="underline text-amber-900 dark:text-amber-300 hover:text-amber-700 dark:hover:text-amber-300"
target="_blank"
rel="noopener noreferrer"
>

View File

@ -237,7 +237,7 @@ export function LlmWorkersForm({ config, onChange, overriddenByEnv }: SectionFor
/>
{endpointOverridden && <EnvOverrideWarning />}
{showSelfLoop && (
<p className="text-2xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-2 py-1 mt-1">
<p className="text-2xs text-amber-700 dark:text-amber-300 bg-amber-50 dark:bg-amber-500/15 border border-amber-200 dark:border-amber-500/30 rounded px-2 py-1 mt-1">
endpoint (self-loop)
</p>

View File

@ -144,11 +144,11 @@ async function fetchMetrics(days: number = 30): Promise<ReflectionMetrics> {
// ── Shared UI primitives ──────────────────────────────────────────────────────
const OUTCOME_LABELS: Record<string, { label: string; cls: string }> = {
applied: { label: '適用済み', cls: 'bg-emerald-100 text-emerald-800' },
partial: { label: '一部適用', cls: 'bg-yellow-100 text-yellow-800' },
applied: { label: '適用済み', cls: 'bg-emerald-100 dark:bg-emerald-500/15 text-emerald-800 dark:text-emerald-300' },
partial: { label: '一部適用', cls: 'bg-yellow-100 dark:bg-yellow-500/15 text-yellow-800 dark:text-yellow-300' },
abstained: { label: '学習なし', cls: 'bg-slate-100 text-slate-600' },
rejected: { label: '却下', cls: 'bg-red-100 text-red-700' },
failed: { label: '失敗', cls: 'bg-red-200 text-red-900' },
rejected: { label: '却下', cls: 'bg-red-100 dark:bg-red-500/15 text-red-700 dark:text-red-300' },
failed: { label: '失敗', cls: 'bg-red-200 dark:bg-red-500/20 text-red-900 dark:text-red-300' },
};
function OutcomeBadge({ outcome }: { outcome: string }) {
@ -307,7 +307,7 @@ function MemoryEntryModal({
</div>
{error && (
<div className="text-xs text-red-600 bg-red-50 border border-red-200 px-3 py-2 rounded-md">
<div className="text-xs text-red-600 dark:text-red-300 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 px-3 py-2 rounded-md">
{error}
</div>
)}
@ -411,7 +411,7 @@ function MemoryEntriesPanel() {
)}
{deleteError && (
<div className="px-4 py-2 text-xs text-red-600 bg-red-50">
<div className="px-4 py-2 text-xs text-red-600 dark:text-red-300 bg-red-50 dark:bg-red-500/15">
{deleteError}
</div>
)}
@ -456,7 +456,7 @@ function MemoryEntriesPanel() {
<button
onClick={() => void handleDelete(entry.name)}
disabled={deleting === entry.name}
className="px-2 h-6 text-2xs text-red-700 border border-red-200 bg-canvas hover:bg-red-50 rounded transition-colors disabled:opacity-50"
className="px-2 h-6 text-2xs text-red-700 dark:text-red-300 border border-red-200 bg-canvas hover:bg-red-50 dark:hover:bg-red-500/15 rounded transition-colors disabled:opacity-50"
>
{deleting === entry.name ? '…' : '削除'}
</button>
@ -517,12 +517,12 @@ function SnapshotCard({ item, onReverted }: { item: SnapshotIndexEntry; onRevert
</span>
<span className="flex-shrink-0 flex items-center gap-1.5">
{item.memoryChanges > 0 && (
<span className="text-[10px] px-1.5 py-0.5 bg-blue-50 text-blue-700 rounded">
<span className="text-[10px] px-1.5 py-0.5 bg-blue-50 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300 rounded">
{item.memoryChanges} mem
</span>
)}
{item.pieceEdited && (
<span className="text-[10px] px-1.5 py-0.5 bg-purple-50 text-purple-700 rounded">
<span className="text-[10px] px-1.5 py-0.5 bg-purple-50 dark:bg-purple-500/15 text-purple-700 dark:text-purple-300 rounded">
piece
</span>
)}
@ -579,7 +579,7 @@ function SnapshotCard({ item, onReverted }: { item: SnapshotIndexEntry; onRevert
</div>
<ul className="space-y-0.5">
{d.rejections.map((r, i) => (
<li key={i} className="text-2xs text-red-700">
<li key={i} className="text-2xs text-red-700 dark:text-red-300">
<span className="font-mono">{r.code}</span>
{r.name && <span className="text-slate-500 ml-1">({r.name})</span>}
</li>
@ -631,7 +631,7 @@ function SnapshotCard({ item, onReverted }: { item: SnapshotIndexEntry; onRevert
{!item.reverted && (
<div className="pt-1">
{revertDone === true && (
<span className="text-xs text-emerald-700"> revert </span>
<span className="text-xs text-emerald-700 dark:text-emerald-300"> revert </span>
)}
{revertDone === false && (
<span className="text-xs text-slate-500"> revert </span>
@ -640,14 +640,14 @@ function SnapshotCard({ item, onReverted }: { item: SnapshotIndexEntry; onRevert
<button
type="button"
onClick={() => setConfirmRevert(true)}
className="px-2.5 h-7 text-2xs text-amber-800 border border-amber-300 bg-amber-50 hover:bg-amber-100 rounded transition-colors"
className="px-2.5 h-7 text-2xs text-amber-800 dark:text-amber-300 border border-amber-300 dark:border-amber-500/30 bg-amber-50 dark:bg-amber-500/15 hover:bg-amber-100 dark:hover:bg-amber-500/15 rounded transition-colors"
>
revert
</button>
)}
{revertDone === null && confirmRevert && (
<div className="flex items-center gap-2">
<span className="text-xs text-amber-800">
<span className="text-xs text-amber-800 dark:text-amber-300">
</span>
<button
@ -729,12 +729,12 @@ function BeforeAfterDiff({
</div>
)}
{isAdded && (
<div className="text-2xs text-emerald-700 bg-emerald-50 border border-emerald-200 rounded px-2 py-1 mb-1">
<div className="text-2xs text-emerald-700 dark:text-emerald-300 bg-emerald-50 dark:bg-emerald-500/15 border border-emerald-200 dark:border-emerald-500/30 rounded px-2 py-1 mb-1">
</div>
)}
{isRemoved && (
<div className="text-2xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 mb-1">
<div className="text-2xs text-red-700 dark:text-red-300 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 rounded px-2 py-1 mb-1">
</div>
)}

View File

@ -146,7 +146,7 @@ export function ModelSelect({ value, onChange, endpoint, apiKeyRaw }: ModelSelec
className="w-full h-8 px-2.5 text-[13px] border border-hairline rounded-md focus:ring-2 focus:ring-accent-ring focus:border-accent outline-none bg-canvas"
/>
{error && (
<p className="text-2xs text-amber-700 bg-amber-50 border border-amber-100 px-2 py-1 rounded mt-1">
<p className="text-2xs text-amber-700 dark:text-amber-300 bg-amber-50 dark:bg-amber-500/15 border border-amber-100 dark:border-amber-500/30 px-2 py-1 rounded mt-1">
{error}
</p>
)}

View File

@ -37,11 +37,11 @@ export function MovementAccordion({ movements, onChange, onAdd, onRemove, onMove
<span className="text-xs text-slate-400 mr-1">{isExpanded ? '\u25BC' : '\u25B6'}</span>
<span className="text-sm font-medium text-slate-800">{movement.name || '(unnamed)'}</span>
{movement.persona && (
<span className="bg-blue-100 text-blue-700 text-xs px-2 py-0.5 rounded">
<span className="bg-blue-100 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300 text-xs px-2 py-0.5 rounded">
{movement.persona}
</span>
)}
<span className={`text-xs px-2 py-0.5 rounded ${movement.edit ? 'bg-green-100 text-green-700' : 'bg-purple-100 text-purple-700'}`}>
<span className={`text-xs px-2 py-0.5 rounded ${movement.edit ? 'bg-green-100 dark:bg-green-500/15 text-green-700 dark:text-green-300' : 'bg-purple-100 dark:bg-purple-500/15 text-purple-700 dark:text-purple-300'}`}>
edit: {movement.edit ? 'on' : 'off'}
</span>
<span className="text-xs text-slate-400">{toolCount} tools</span>
@ -108,7 +108,7 @@ export function MovementAccordion({ movements, onChange, onAdd, onRemove, onMove
<button
type="button"
onClick={onAdd}
className="mt-3 text-sm text-blue-600 hover:text-blue-700"
className="mt-3 text-sm text-blue-600 hover:text-blue-700 dark:hover:text-blue-300"
>
+ Add Movement
</button>

View File

@ -379,7 +379,7 @@ export function NotificationsForm() {
<button
onClick={() => handleDeleteRemote(s.id)}
disabled={busy}
className="ml-2 px-2 py-1 text-[11px] text-red-700 hover:bg-red-50 rounded"
className="ml-2 px-2 py-1 text-[11px] text-red-700 dark:text-red-300 hover:bg-red-50 dark:hover:bg-red-500/15 rounded"
>
</button>
@ -403,7 +403,7 @@ export function NotificationsForm() {
)}
{pushFatal && (
<p className="text-[12px] text-red-700">: {pushFatal}</p>
<p className="text-[12px] text-red-700 dark:text-red-300">: {pushFatal}</p>
)}
</div>
)}

View File

@ -224,7 +224,7 @@ export function PieceEditor({ name, isAdmin = true, source }: PieceEditorProps)
<button
type="button"
onClick={handleDelete}
className="px-3 py-1.5 text-xs text-red-600 hover:bg-red-50 rounded-lg border border-red-200"
className="px-3 py-1.5 text-xs text-red-600 hover:bg-red-50 dark:hover:bg-red-500/15 rounded-lg border border-red-200"
>
Delete
</button>
@ -288,7 +288,7 @@ export function PieceEditor({ name, isAdmin = true, source }: PieceEditorProps)
/* YAML editor */
<div className="mb-6">
{yamlError && (
<div className="mb-2 px-3 py-2 bg-red-50 border border-red-200 rounded-lg text-xs text-red-600">
<div className="mb-2 px-3 py-2 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 rounded-lg text-xs text-red-600 dark:text-red-300">
{yamlError}
</div>
)}

View File

@ -53,7 +53,7 @@ export function ReflectionForm({ config, onChange }: SectionFormProps) {
</div>
{enabled && !hasReflectionWorker && (
<div className="rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-900">
<div className="rounded-md border border-amber-300 dark:border-amber-500/30 bg-amber-50 dark:bg-amber-500/15 px-3 py-2 text-xs text-amber-900 dark:text-amber-300">
<div className="font-semibold mb-1"> Reflection worker </div>
<div>
Reflection <code className="font-mono">roles</code>

View File

@ -83,7 +83,7 @@ export function RulesTable({ rules, movementNames, onChange, disabled = false }:
<button
type="button"
onClick={addRule}
className="text-xs text-blue-600 hover:text-blue-700"
className="text-xs text-blue-600 hover:text-blue-700 dark:hover:text-blue-300"
>
+ Add Rule
</button>

View File

@ -140,7 +140,7 @@ export function SecretInput({ rawValue, onChange, placeholder }: SecretInputProp
readOnly
className="flex-1 h-8 px-2.5 text-[13px] border border-hairline rounded-md bg-slate-50 text-slate-400 italic"
/>
<span className="inline-flex items-center px-2 h-6 text-2xs rounded bg-amber-50 text-amber-700 border border-amber-200">
<span className="inline-flex items-center px-2 h-6 text-2xs rounded bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-200 dark:border-amber-500/30">
will be cleared
</span>
</div>
@ -180,7 +180,7 @@ export function SecretInput({ rawValue, onChange, placeholder }: SecretInputProp
<button
type="button"
onClick={setClearedMode}
className="px-2 py-0.5 rounded border border-amber-200 text-amber-700 hover:bg-amber-50"
className="px-2 py-0.5 rounded border border-amber-200 text-amber-700 dark:text-amber-300 hover:bg-amber-50 dark:hover:bg-amber-500/15"
>
Clear
</button>

View File

@ -29,7 +29,7 @@ function SourceBadge({ source }: { source: 'system' | 'user' }) {
<span aria-label="locked">&#128274;</span> system
</span>
) : (
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-semibold bg-blue-50 text-blue-700">
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-semibold bg-blue-50 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300">
user
</span>
);
@ -37,10 +37,10 @@ function SourceBadge({ source }: { source: 'system' | 'user' }) {
function SeverityBadge({ severity }: { severity: string }) {
if (severity === 'high') {
return <span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-semibold bg-red-100 text-red-700">HIGH</span>;
return <span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-semibold bg-red-100 dark:bg-red-500/15 text-red-700 dark:text-red-300">HIGH</span>;
}
if (severity === 'medium') {
return <span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-semibold bg-yellow-100 text-yellow-800">MEDIUM</span>;
return <span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-semibold bg-yellow-100 dark:bg-yellow-500/15 text-yellow-800 dark:text-yellow-300">MEDIUM</span>;
}
return null;
}
@ -178,7 +178,7 @@ export function SkillsForm() {
</p>
{error && (
<div className="mb-3 px-3 py-2 rounded bg-red-50 border border-red-200 text-xs text-red-700">
<div className="mb-3 px-3 py-2 rounded bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 text-xs text-red-700 dark:text-red-300">
{error}
</div>
)}
@ -331,10 +331,10 @@ export function SkillsForm() {
{/* Findings */}
{detailQuery.data.findings.length > 0 && (
<div className="border border-yellow-200 bg-yellow-50 rounded p-2 flex flex-col gap-1">
<div className="text-xs font-semibold text-yellow-800">Security Findings</div>
<div className="border border-yellow-200 dark:border-yellow-500/30 bg-yellow-50 dark:bg-yellow-500/15 rounded p-2 flex flex-col gap-1">
<div className="text-xs font-semibold text-yellow-800 dark:text-yellow-300">Security Findings</div>
{detailQuery.data.findings.map((f, i) => (
<div key={i} className="text-[10px] text-yellow-700 font-mono">
<div key={i} className="text-[10px] text-yellow-700 dark:text-yellow-300 font-mono">
<SeverityBadge severity={f.severity} />{' '}
L{f.line}: {f.pattern} &mdash; <code>{f.match}</code>
{f.file && <span className="text-slate-500"> ({f.file})</span>}
@ -396,7 +396,7 @@ export function SkillsForm() {
<button
onClick={handleDelete}
disabled={deleteMut.isPending}
className="px-3 py-1.5 text-xs font-semibold text-red-600 border border-red-200 rounded hover:bg-red-50 disabled:opacity-50 transition-colors"
className="px-3 py-1.5 text-xs font-semibold text-red-600 border border-red-200 rounded hover:bg-red-50 dark:hover:bg-red-500/15 disabled:opacity-50 transition-colors"
>
{deleteMut.isPending ? 'Deleting...' : 'Delete'}
</button>

View File

@ -174,9 +174,9 @@ export function SshAuditLog() {
function OutcomeBadge({ outcome }: { outcome: string }) {
const colorMap: Record<string, string> = {
pending: 'bg-slate-100 text-slate-600',
success: 'bg-emerald-50 text-emerald-700',
failed: 'bg-red-50 text-red-700',
denied: 'bg-amber-50 text-amber-700',
success: 'bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300',
failed: 'bg-red-50 dark:bg-red-500/15 text-red-700 dark:text-red-300',
denied: 'bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300',
aborted: 'bg-slate-100 text-slate-500',
};
const cls = colorMap[outcome] ?? 'bg-slate-100 text-slate-600';

View File

@ -258,7 +258,7 @@ export function SshGlobalConnectionsForm({ showToast, onChange }: Props) {
)}
</div>
{c.disabledByAdminReason && (
<div className="text-2xs text-red-700 mt-0.5">: {c.disabledByAdminReason}</div>
<div className="text-2xs text-red-700 dark:text-red-300 mt-0.5">: {c.disabledByAdminReason}</div>
)}
</div>
<div className="flex items-center gap-1 flex-shrink-0 flex-wrap justify-end max-w-[280px]">
@ -371,8 +371,8 @@ function ReasonModal({ title, warning, onCancel, onSubmit }: ReasonModalProps) {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4">
<div className="w-full max-w-md bg-surface rounded-md shadow-lg border border-hairline overflow-hidden">
<div className={`px-4 py-3 border-b border-hairline ${warning ? 'bg-red-50' : ''}`}>
<h3 className={`text-sm font-semibold ${warning ? 'text-red-800' : 'text-slate-900'}`}>{title}</h3>
<div className={`px-4 py-3 border-b border-hairline ${warning ? 'bg-red-50 dark:bg-red-500/15' : ''}`}>
<h3 className={`text-sm font-semibold ${warning ? 'text-red-800 dark:text-red-300' : 'text-slate-900'}`}>{title}</h3>
</div>
<div className="px-4 py-3 space-y-2">
<label className="block text-2xs font-semibold text-slate-500 uppercase tracking-wide">
@ -404,7 +404,7 @@ function ReasonModal({ title, warning, onCancel, onSubmit }: ReasonModalProps) {
}
const btnCls = 'px-2 h-7 text-2xs text-slate-700 border border-hairline rounded hover:bg-surface disabled:opacity-50';
const btnDangerCls = 'px-2 h-7 text-2xs text-red-600 border border-hairline rounded hover:bg-red-50 disabled:opacity-50';
const btnDangerCls = 'px-2 h-7 text-2xs text-red-600 border border-hairline rounded hover:bg-red-50 dark:hover:bg-red-500/15 disabled:opacity-50';
/**
* Click-to-copy connection UUID. Same UX as the user folder panel agents
@ -436,10 +436,10 @@ function CopyableUuid({ value }: { value: string }) {
function Badge({ color, children }: { color: 'slate' | 'blue' | 'emerald' | 'amber' | 'red'; children: React.ReactNode }) {
const cls: Record<typeof color, string> = {
slate: 'bg-slate-100 text-slate-600',
blue: 'bg-blue-50 text-blue-600',
emerald: 'bg-emerald-50 text-emerald-700',
amber: 'bg-amber-50 text-amber-700',
red: 'bg-red-50 text-red-700',
blue: 'bg-blue-50 dark:bg-blue-500/15 text-blue-600 dark:text-blue-300',
emerald: 'bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300',
amber: 'bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300',
red: 'bg-red-50 dark:bg-red-500/15 text-red-700 dark:text-red-300',
};
return (
<span className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-medium leading-none ${cls[color]}`}>

View File

@ -105,7 +105,7 @@ export function SshGrantsForm({ showToast }: Props) {
return (
<div className="space-y-4">
<h3 className="text-sm font-semibold text-slate-900"> (grants)</h3>
<div className="border border-amber-200 rounded-md bg-amber-50 p-4 text-xs text-amber-900">
<div className="border border-amber-200 dark:border-amber-500/30 rounded-md bg-amber-50 dark:bg-amber-500/15 p-4 text-xs text-amber-900 dark:text-amber-300">
<div className="font-semibold mb-1">SSH </div>
<div>
<code className="font-mono">config.yaml</code> <code className="font-mono">ssh.enabled: true</code>
@ -169,7 +169,7 @@ export function SshGrantsForm({ showToast }: Props) {
<div className="text-2xs">
<span className="font-mono font-semibold">{g.subjectType}:{g.subjectId}</span>
{g.appliesToAllPieces ? (
<span className="ml-2 inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-amber-50 text-amber-700">
<span className="ml-2 inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300">
all pieces
</span>
) : (
@ -183,7 +183,7 @@ export function SshGrantsForm({ showToast }: Props) {
</div>
<button
onClick={() => setReasonForDelete(g)}
className="px-2 h-6 text-2xs text-red-600 border border-hairline rounded hover:bg-red-50"
className="px-2 h-6 text-2xs text-red-600 border border-hairline rounded hover:bg-red-50 dark:hover:bg-red-500/15"
>
</button>
@ -303,7 +303,7 @@ function CreateGrantForm({ connections, pieces, onSubmit, onCancel }: CreateGran
/>
<span>
<span className="font-semibold"> (applies_to_all_pieces)</span>
<span className="block text-2xs text-amber-700">
<span className="block text-2xs text-amber-700 dark:text-amber-300">
grant piece 使
</span>
</span>
@ -386,8 +386,8 @@ function ReasonModal({ title, warning, onCancel, onSubmit }: ReasonModalProps) {
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4">
<div className="w-full max-w-md bg-surface rounded-md shadow-lg border border-hairline overflow-hidden">
<div className={`px-4 py-3 border-b border-hairline ${warning ? 'bg-red-50' : ''}`}>
<h3 className={`text-sm font-semibold ${warning ? 'text-red-800' : 'text-slate-900'}`}>{title}</h3>
<div className={`px-4 py-3 border-b border-hairline ${warning ? 'bg-red-50 dark:bg-red-500/15' : ''}`}>
<h3 className={`text-sm font-semibold ${warning ? 'text-red-800 dark:text-red-300' : 'text-slate-900'}`}>{title}</h3>
</div>
<div className="px-4 py-3 space-y-2">
<label className="block text-2xs font-semibold text-slate-500 uppercase tracking-wide">Reason ( 8 chars)</label>

View File

@ -99,13 +99,13 @@ export function SshMasterKeyRotationForm({ showToast }: Props) {
{activeJobId === null && <span>idle (rotation )</span>}
{activeJobId !== null && statusQuery.isLoading && <span className="text-slate-400"></span>}
{activeJobId !== null && status === null && (
<span className="text-emerald-700">job {activeJobId} </span>
<span className="text-emerald-700 dark:text-emerald-300">job {activeJobId} </span>
)}
{status && (
<>
<span className="font-mono">{status.status}</span>
{status.notImplemented && (
<span className="ml-2 inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-amber-50 text-amber-700">
<span className="ml-2 inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300">
stub (v1)
</span>
)}
@ -169,8 +169,8 @@ function ConfirmDialog({
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4">
<div className="w-full max-w-lg bg-surface rounded-md shadow-lg border border-amber-300 overflow-hidden">
<div className="px-4 py-3 border-b border-amber-200 bg-amber-50">
<h3 className="text-sm font-semibold text-amber-900"> Master Key Rotation </h3>
<div className="px-4 py-3 border-b border-amber-200 dark:border-amber-500/30 bg-amber-50 dark:bg-amber-500/15">
<h3 className="text-sm font-semibold text-amber-900 dark:text-amber-300"> Master Key Rotation </h3>
</div>
<div className="px-4 py-3 space-y-3">
<p className="text-xs text-slate-700 leading-relaxed">

View File

@ -221,7 +221,7 @@ function SelectedToolChip({
// - unknown (not in catalog at all) → amber chip + "unknown" badge
const tone =
isUnknown || isUnavailable
? 'bg-amber-50 text-amber-800 border border-amber-200'
? 'bg-amber-50 dark:bg-amber-500/15 text-amber-800 dark:text-amber-300 border border-amber-200 dark:border-amber-500/30'
: 'bg-slate-100 text-slate-700';
const tip = isUnknown
? 'このツールは現在のカタログに存在しません。明示削除するまで保持されます。'
@ -272,10 +272,10 @@ function Badge({
}) {
const cls: Record<typeof color, string> = {
slate: 'bg-slate-100 text-slate-600',
blue: 'bg-blue-50 text-blue-700',
emerald: 'bg-emerald-50 text-emerald-700',
amber: 'bg-amber-50 text-amber-700',
red: 'bg-red-50 text-red-700',
blue: 'bg-blue-50 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300',
emerald: 'bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300',
amber: 'bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300',
red: 'bg-red-50 dark:bg-red-500/15 text-red-700 dark:text-red-300',
};
return (
<span

View File

@ -218,7 +218,7 @@ export function ToolsForm({ config, onChange, visibleTabs }: ToolsFormProps) {
<div className="flex items-center gap-2">
<h3 className="text-sm font-semibold text-slate-800">Knowledge (DKS)</h3>
<span
className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold uppercase tracking-wide bg-amber-100 text-amber-800 border border-amber-300"
className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-semibold uppercase tracking-wide bg-amber-100 dark:bg-amber-500/15 text-amber-800 dark:text-amber-300 border border-amber-300 dark:border-amber-500/30"
title="この機能は legacy です。新規の知識検索統合は MCP server 経由を推奨"
>
LEGACY
@ -226,14 +226,14 @@ export function ToolsForm({ config, onChange, visibleTabs }: ToolsFormProps) {
</div>
<div
role="note"
className="rounded border border-amber-300 bg-amber-50 px-3 py-2 text-xs text-amber-900"
className="rounded border border-amber-300 dark:border-amber-500/30 bg-amber-50 dark:bg-amber-500/15 px-3 py-2 text-xs text-amber-900 dark:text-amber-300"
>
DKS <strong>legacy</strong> {' '}
<strong>MCP server </strong> namespace
namespace {' '}
<a
href="/help"
className="underline text-amber-900 hover:text-amber-700"
className="underline text-amber-900 dark:text-amber-300 hover:text-amber-700 dark:hover:text-amber-300"
target="_blank"
rel="noopener noreferrer"
>

View File

@ -1,6 +1,6 @@
export function EnvOverrideWarning() {
return (
<div className="text-2xs text-amber-700 bg-amber-50 border border-amber-100 px-2 py-1 rounded mt-1">
<div className="text-2xs text-amber-700 dark:text-amber-300 bg-amber-50 dark:bg-amber-500/15 border border-amber-100 dark:border-amber-500/30 px-2 py-1 rounded mt-1">
</div>
);

View File

@ -43,7 +43,7 @@ export function EmptyState({ title, description, hint, compact, action, onCreate
'コメントを送ると追加指示として処理される',
].map((step, i) => (
<li key={i} className="flex gap-3 items-start text-xs text-slate-500">
<span className="flex-shrink-0 w-5 h-5 rounded-full bg-blue-100 text-blue-700 text-[10px] font-bold flex items-center justify-center mt-0.5">
<span className="flex-shrink-0 w-5 h-5 rounded-full bg-blue-100 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300 text-[10px] font-bold flex items-center justify-center mt-0.5">
{i + 1}
</span>
{step}

View File

@ -78,7 +78,7 @@ export function AgentsMdPanel({ onDirtyChange }: AgentsMdPanelProps) {
{data?.exists && (
<button
type="button"
className="text-2xs text-red-600 hover:text-red-800 underline"
className="text-2xs text-red-600 hover:text-red-800 dark:hover:text-red-300 underline"
onClick={() => {
if (window.confirm('AGENTS.md を削除しますか?')) del.mutate();
}}

View File

@ -9,10 +9,10 @@ import { AddBrowserSessionDialog } from './AddBrowserSessionDialog';
function StatusPill({ status }: { status: BrowserSessionProfile['status'] }) {
const map: Record<BrowserSessionProfile['status'], string> = {
pending: 'bg-slate-200 text-slate-700',
active: 'bg-emerald-100 text-emerald-700',
expired: 'bg-amber-100 text-amber-800',
active: 'bg-emerald-100 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300',
expired: 'bg-amber-100 dark:bg-amber-500/15 text-amber-800 dark:text-amber-300',
revoked: 'bg-slate-200 text-slate-500',
error: 'bg-rose-100 text-rose-700',
error: 'bg-rose-100 dark:bg-rose-500/15 text-rose-700 dark:text-rose-300',
};
const labels: Record<BrowserSessionProfile['status'], string> = {
pending: '保留中',
@ -87,7 +87,7 @@ export function BrowserSessionsPanel() {
<button onClick={() => { setReLoginProfileId(p.id); setAdding(true); }}
className="text-xs text-slate-700 hover:text-slate-900 px-2 py-1 rounded hover:bg-surface"></button>
<button onClick={() => { if (confirm(`${p.label} を削除しますか?`)) del.mutate(p.id); }}
className="text-xs text-rose-600 hover:text-rose-800 px-2 py-1 rounded hover:bg-rose-50"></button>
className="text-xs text-rose-600 hover:text-rose-800 dark:hover:text-rose-300 px-2 py-1 rounded hover:bg-rose-50 dark:hover:bg-rose-500/15"></button>
</div>
</div>
))}

View File

@ -132,7 +132,7 @@ export function FileTree({
e.stopPropagation();
onDeleteFile(subdir, file.name);
}}
className={`flex-shrink-0 w-4 h-4 flex items-center justify-center rounded hover:bg-red-100 hover:text-red-600 transition-colors ${
className={`flex-shrink-0 w-4 h-4 flex items-center justify-center rounded hover:bg-red-100 dark:hover:bg-red-500/15 hover:text-red-600 transition-colors ${
isSelected ? 'text-accent-fg/70' : 'text-slate-400'
}`}
>

View File

@ -36,7 +36,7 @@ function OwnerBadge({ ownerId }: { ownerId: string | null }) {
);
}
return (
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-blue-50 text-blue-600 leading-none">
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-blue-50 dark:bg-blue-500/15 text-blue-600 dark:text-blue-300 leading-none">
personal
</span>
);

View File

@ -233,7 +233,7 @@ function ScopeBadge({ ownerId }: { ownerId: string | null }) {
return ownerId === null ? (
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-slate-100 text-slate-500 leading-none">global</span>
) : (
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-blue-50 text-blue-600 leading-none">personal</span>
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-blue-50 dark:bg-blue-500/15 text-blue-600 dark:text-blue-300 leading-none">personal</span>
);
}
@ -341,10 +341,10 @@ export function McpPanel({ showToast }: { showToast?: ShowToast }) {
<span className="text-[13px] font-medium text-slate-900">{s.name}</span>
<ScopeBadge ownerId={s.ownerId} />
<span className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-medium leading-none ${
s.authKind === 'oauth' ? 'bg-purple-50 text-purple-600' : 'bg-amber-50 text-amber-600'
s.authKind === 'oauth' ? 'bg-purple-50 dark:bg-purple-500/15 text-purple-600 dark:text-purple-300' : 'bg-amber-50 dark:bg-amber-500/15 text-amber-600 dark:text-amber-300'
}`}>{s.authKind === 'oauth' ? 'OAuth' : 'API key'}</span>
{s.toolCount != null && s.toolCount > 0 && (
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-green-50 text-green-700 leading-none">
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-green-50 dark:bg-green-500/15 text-green-700 dark:text-green-300 leading-none">
{s.toolCount}
</span>
)}
@ -372,7 +372,7 @@ export function McpPanel({ showToast }: { showToast?: ShowToast }) {
onClick={() => setEditingId(s.id)}>
</button>
<button type="button" className="text-2xs text-red-600 hover:text-red-800 underline"
<button type="button" className="text-2xs text-red-600 hover:text-red-800 dark:hover:text-red-300 underline"
onClick={() => handleDelete(s.id, s.name, isGlobal)}
disabled={delMut.isPending}>

View File

@ -311,8 +311,8 @@ function ServerTable({
<td className="py-2 pr-2">
<span className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-medium leading-none ${
s.authKind === 'oauth'
? 'bg-purple-50 text-purple-600'
: 'bg-amber-50 text-amber-600'
? 'bg-purple-50 dark:bg-purple-500/15 text-purple-600 dark:text-purple-300'
: 'bg-amber-50 dark:bg-amber-500/15 text-amber-600 dark:text-amber-300'
}`}>
{s.authKind === 'oauth' ? 'OAuth' : 'API key'}
</span>
@ -322,7 +322,7 @@ function ServerTable({
{s.toolCount == null || s.toolCount === 0 ? (
<span className="text-[10px] text-slate-400 italic"> </span>
) : (
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-green-50 text-green-700 leading-none">
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-green-50 dark:bg-green-500/15 text-green-700 dark:text-green-300 leading-none">
{s.toolCount}
</span>
)}
@ -337,7 +337,7 @@ function ServerTable({
{canDelete && (
<button
type="button"
className="text-2xs text-red-600 hover:text-red-800 underline"
className="text-2xs text-red-600 hover:text-red-800 dark:hover:text-red-300 underline"
onClick={() => onDelete(s.id, s.name, isGlobal)}
disabled={deletePending}
></button>
@ -484,7 +484,7 @@ export function McpServersPanel({ showToast }: McpServersPanelProps = {}) {
<section className="space-y-4">
<div className="flex items-center gap-2">
<h3 className="text-[13px] font-semibold text-slate-900"></h3>
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-blue-50 text-blue-600 leading-none">
<span className="inline-block px-1.5 py-0.5 rounded text-[10px] font-medium bg-blue-50 dark:bg-blue-500/15 text-blue-600 dark:text-blue-300 leading-none">
personal
</span>
</div>

View File

@ -104,7 +104,7 @@ export function MonacoFileEditor({ subdir, filename, content, mtime, size, onSav
{filename}
</span>
{dirty && (
<span className="text-[10px] font-medium text-amber-600 bg-amber-50 border border-amber-200 rounded px-1.5 py-0.5">
<span className="text-[10px] font-medium text-amber-600 dark:text-amber-300 bg-amber-50 dark:bg-amber-500/15 border border-amber-200 dark:border-amber-500/30 rounded px-1.5 py-0.5">
unsaved
</span>
)}

View File

@ -393,7 +393,7 @@ export function PetsPanel({ showToast }: { showToast?: ShowToast }) {
<span className="inline-block w-3 text-slate-400">{collapsed ? '▶' : '▼'}</span>
{' '}
{worker.id}
<span className="ml-2 px-1.5 py-0.5 rounded text-[10px] font-medium bg-violet-50 text-violet-700">
<span className="ml-2 px-1.5 py-0.5 rounded text-[10px] font-medium bg-violet-50 dark:bg-violet-500/15 text-violet-700 dark:text-violet-300">
proxy
</span>
</div>
@ -461,7 +461,7 @@ export function PetsPanel({ showToast }: { showToast?: ShowToast }) {
<div className="flex items-center gap-2">
<span className="text-[13px] font-semibold text-slate-900 truncate">{pet.name}</span>
{active && (
<span className="px-1.5 py-0.5 rounded text-[10px] font-medium bg-emerald-50 text-emerald-700">
<span className="px-1.5 py-0.5 rounded text-[10px] font-medium bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300">
default
</span>
)}

View File

@ -274,7 +274,7 @@ export function SaveAsScriptDialog({ recordingName, onClose, onSuccess }: SaveAs
type="button"
onClick={() => removeHint(idx)}
aria-label="Remove hint"
className="mt-1 w-5 h-5 flex-shrink-0 flex items-center justify-center rounded hover:bg-red-100 hover:text-red-600 text-slate-400 transition-colors"
className="mt-1 w-5 h-5 flex-shrink-0 flex items-center justify-center rounded hover:bg-red-100 dark:hover:bg-red-500/15 hover:text-red-600 text-slate-400 transition-colors"
>
<svg viewBox="0 0 16 16" className="w-3 h-3" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
<path d="M4 4l8 8M12 4l-8 8" />
@ -297,7 +297,7 @@ export function SaveAsScriptDialog({ recordingName, onClose, onSuccess }: SaveAs
{/* Errors */}
{(validationError || conflictError || submitError) && (
<div className="px-3 py-2 rounded-md bg-red-50 border border-red-200 text-xs text-red-700">
<div className="px-3 py-2 rounded-md bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 text-xs text-red-700 dark:text-red-300">
{validationError ?? conflictError ?? submitError}
</div>
)}

View File

@ -324,8 +324,8 @@ export function SshConnectionForm({ existing, adminContext, onSubmit, onCancel }
</div>
{adminContext && (
<fieldset className="rounded border border-amber-200 bg-amber-50/50 p-3">
<legend className="px-1 text-2xs font-semibold text-amber-800 uppercase tracking-wide">
<fieldset className="rounded border border-amber-200 dark:border-amber-500/30 bg-amber-50/50 dark:bg-amber-500/15 p-3">
<legend className="px-1 text-2xs font-semibold text-amber-800 dark:text-amber-300 uppercase tracking-wide">
Admin-only flags
</legend>
<label className="flex items-start gap-2 text-xs cursor-pointer">

View File

@ -408,7 +408,7 @@ function ConnectionRow(props: ConnectionRowProps) {
)}
</div>
{c.disabledByAdminReason && (
<div className="text-2xs text-red-700 mt-0.5">: {c.disabledByAdminReason}</div>
<div className="text-2xs text-red-700 dark:text-red-300 mt-0.5">: {c.disabledByAdminReason}</div>
)}
</div>
<div className="flex items-center gap-1 flex-shrink-0 flex-wrap justify-end">
@ -442,7 +442,7 @@ function ConnectionRow(props: ConnectionRowProps) {
<button
type="button"
onClick={onDelete}
className="px-2 h-7 text-2xs text-red-600 border border-hairline rounded hover:bg-red-50"
className="px-2 h-7 text-2xs text-red-600 border border-hairline rounded hover:bg-red-50 dark:hover:bg-red-500/15"
>
</button>
@ -508,10 +508,10 @@ function HostKeyBadge({ verified, pending }: { verified: boolean; pending: boole
function Badge({ color, children }: { color: 'slate' | 'blue' | 'emerald' | 'amber' | 'red'; children: React.ReactNode }) {
const cls: Record<typeof color, string> = {
slate: 'bg-slate-100 text-slate-600',
blue: 'bg-blue-50 text-blue-600',
emerald: 'bg-emerald-50 text-emerald-700',
amber: 'bg-amber-50 text-amber-700',
red: 'bg-red-50 text-red-700',
blue: 'bg-blue-50 dark:bg-blue-500/15 text-blue-600 dark:text-blue-300',
emerald: 'bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300',
amber: 'bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300',
red: 'bg-red-50 dark:bg-red-500/15 text-red-700 dark:text-red-300',
};
return (
<span className={`inline-block px-1.5 py-0.5 rounded text-[10px] font-medium leading-none ${cls[color]}`}>

View File

@ -39,7 +39,7 @@ export function SshPublicKeyDialog({ publicKey, label, freshlyGenerated, onClose
{label && <span className="font-normal text-slate-500"> {label}</span>}
</h3>
{freshlyGenerated && (
<p className="text-2xs text-emerald-700 mt-1">
<p className="text-2xs text-emerald-700 dark:text-emerald-300 mt-1">
Orchestrator
<code className="font-mono"> ~/.ssh/authorized_keys</code>
</p>

View File

@ -381,7 +381,7 @@ export function SubscriptionsPanel({ currentUserId }: { currentUserId: string })
/>
<button
type="button"
className="text-2xs text-red-600 hover:text-red-800 font-medium px-2 py-0.5 rounded hover:bg-red-50 transition-colors"
className="text-2xs text-red-600 hover:text-red-800 dark:hover:text-red-300 font-medium px-2 py-0.5 rounded hover:bg-red-50 dark:hover:bg-red-500/15 transition-colors"
onClick={() =>
unsubscribe.mutate({ publisher: s.publisher_user_id, folder: s.folder })
}

View File

@ -167,7 +167,7 @@
@apply bg-canvas text-slate-700 border-hairline hover:bg-surface;
}
.btn-danger {
@apply bg-canvas text-red-700 border-red-200 hover:bg-red-50;
@apply bg-canvas text-red-700 border-red-200 hover:bg-red-50 dark:hover:bg-red-500/15;
}
.chat-pet-overlay {

26
ui/src/lib/utils.test.ts Normal file
View File

@ -0,0 +1,26 @@
import { describe, it, expect } from 'vitest';
import { statusTone, toneClasses } from './utils';
describe('statusTone', () => {
it('returns theme-adaptive light-dark() colors', () => {
const t = statusTone('failed');
expect(t.bg).toBe('light-dark(#fee2e2, rgba(239,68,68,.15))');
expect(t.fg).toBe('light-dark(#b91c1c, #fca5a5)');
});
it('falls back to neutral surface/muted vars', () => {
const t = statusTone('whatever-unknown');
expect(t.bg).toBe('light-dark(#e2e8f0, var(--surface-2))');
expect(t.fg).toBe('light-dark(#475569, var(--muted))');
});
});
describe('toneClasses', () => {
it('pairs light + dark for colored tones', () => {
expect(toneClasses('danger')).toContain('bg-red-50');
expect(toneClasses('danger')).toContain('dark:text-red-300');
expect(toneClasses('success')).toContain('dark:bg-emerald-500/15');
});
it('neutral stays on semantic tokens (auto-themed, no dark: needed)', () => {
expect(toneClasses('neutral')).toBe('bg-surface-2 text-slate-700 border-hairline');
});
});

View File

@ -39,14 +39,40 @@ export function stateTone(state: string): { bg: string; fg: string } {
return { bg: '#dbeafe', fg: '#1e3a8a' };
}
/**
* Status badge colors. Returned values are CSS `light-dark()` pairs so the
* inline-styled badges (style={{ background, color }}) adapt to the theme
* `color-scheme` is set on :root / [data-theme=dark] (see index.css), which is
* what light-dark() resolves against. Dark = a subtle tinted chip + light text.
*/
export function statusTone(status: string): { bg: string; fg: string } {
if (status === 'running') return { bg: '#dcfce7', fg: '#166534' };
if (status === 'waiting_human') return { bg: '#fef9c3', fg: '#854d0e' };
if (status === 'waiting_subtasks') return { bg: '#e0e7ff', fg: '#3730a3' };
if (status === 'failed') return { bg: '#fee2e2', fg: '#b91c1c' };
if (status === 'succeeded') return { bg: '#dbeafe', fg: '#1e40af' };
if (status === 'retry') return { bg: '#fef3c7', fg: '#92400e' };
return { bg: '#e2e8f0', fg: '#475569' };
const ld = (l: string, d: string) => `light-dark(${l}, ${d})`;
if (status === 'running') return { bg: ld('#dcfce7', 'rgba(34,197,94,.15)'), fg: ld('#166534', '#86efac') };
if (status === 'waiting_human') return { bg: ld('#fef9c3', 'rgba(234,179,8,.15)'), fg: ld('#854d0e', '#fde047') };
if (status === 'waiting_subtasks') return { bg: ld('#e0e7ff', 'rgba(99,102,241,.18)'), fg: ld('#3730a3', '#a5b4fc') };
if (status === 'failed') return { bg: ld('#fee2e2', 'rgba(239,68,68,.15)'), fg: ld('#b91c1c', '#fca5a5') };
if (status === 'succeeded') return { bg: ld('#dbeafe', 'rgba(59,130,246,.18)'), fg: ld('#1e40af', '#93c5fd') };
if (status === 'retry') return { bg: ld('#fef3c7', 'rgba(245,158,11,.15)'), fg: ld('#92400e', '#fcd34d') };
return { bg: ld('#e2e8f0', 'var(--surface-2)'), fg: ld('#475569', 'var(--muted)') };
}
export type Tone = 'neutral' | 'info' | 'success' | 'warning' | 'danger';
/**
* Shared light+dark Tailwind classes for semantic badge/chip tones. One source
* of truth so badges stay consistent in both themes. `dark:` follows the
* in-app [data-theme] (tailwind darkMode is the selector variant).
*/
const TONE_CLASSES: Record<Tone, string> = {
neutral: 'bg-surface-2 text-slate-700 border-hairline',
info: 'bg-blue-50 text-blue-700 border-blue-200 dark:bg-blue-500/15 dark:text-blue-300 dark:border-blue-500/30',
success: 'bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-500/15 dark:text-emerald-300 dark:border-emerald-500/30',
warning: 'bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-500/15 dark:text-amber-300 dark:border-amber-500/30',
danger: 'bg-red-50 text-red-700 border-red-200 dark:bg-red-500/15 dark:text-red-300 dark:border-red-500/30',
};
export function toneClasses(tone: Tone): string {
return TONE_CLASSES[tone];
}
export function formatStatusLabel(status: string): string {

View File

@ -76,7 +76,7 @@ export function AdminCaptchaPage({ isAdmin }: { isAdmin: boolean }) {
const msg = error instanceof Error ? error.message : String(error);
return (
<div className="h-full flex items-center justify-center p-8">
<div className="max-w-md text-center text-sm text-red-700">
<div className="max-w-md text-center text-sm text-red-700 dark:text-red-300">
CAPTCHA Pool : {msg}
</div>
</div>
@ -90,7 +90,7 @@ export function AdminCaptchaPage({ isAdmin }: { isAdmin: boolean }) {
<h1 className="text-base font-semibold text-slate-900">CAPTCHA Pool</h1>
<span className="text-2xs font-mono text-slate-400">admin</span>
{data?.available && data.captchaPending && (
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-2xs font-medium border border-amber-300 bg-amber-50 text-amber-800 animate-pulse">
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded-md text-2xs font-medium border border-amber-300 dark:border-amber-500/30 bg-amber-50 dark:bg-amber-500/15 text-amber-800 dark:text-amber-300 animate-pulse">
CAPTCHA
</span>
)}

View File

@ -38,7 +38,7 @@ function DriftBadge({ drift }: { drift: DriftStatus }) {
type="button"
onClick={(e) => { e.stopPropagation(); setOpen(p => !p); }}
title="組み込み Piece がフォーク後に更新されました"
className="px-1.5 py-0.5 text-[10px] font-semibold rounded bg-amber-100 text-amber-800 hover:bg-amber-200 transition-colors leading-none border border-amber-300"
className="px-1.5 py-0.5 text-[10px] font-semibold rounded bg-amber-100 dark:bg-amber-500/15 text-amber-800 dark:text-amber-300 hover:bg-amber-200 dark:hover:bg-amber-500/25 transition-colors leading-none border border-amber-300 dark:border-amber-500/30"
>
updated
</button>
@ -55,7 +55,7 @@ function DriftBadge({ drift }: { drift: DriftStatus }) {
</div>
<div className="flex items-center justify-between gap-2">
<span className="text-slate-500"></span>
<code className="font-mono text-[10px] bg-amber-50 px-1 rounded text-amber-800">
<code className="font-mono text-[10px] bg-amber-50 dark:bg-amber-500/15 px-1 rounded text-amber-800 dark:text-amber-300">
{shortSha(drift.latestCommit)}
</code>
</div>
@ -308,7 +308,7 @@ function PiecesSidebar({
className="w-full h-8 px-2 text-xs border border-hairline rounded-md focus:outline-none focus:ring-2 focus:ring-accent-ring focus:border-accent"
/>
{duplicateError && (
<div role="alert" className="mt-2 text-2xs text-red-700">{duplicateError}</div>
<div role="alert" className="mt-2 text-2xs text-red-700 dark:text-red-300">{duplicateError}</div>
)}
<div className="mt-3 flex justify-end gap-2">
<button

View File

@ -521,15 +521,15 @@ function ScheduleListItem({ task, active, onClick }: { task: ScheduledTask; acti
<span className="text-slate-400">system</span>
)}
{task.visibility === 'private' && (
<span className="px-1 rounded text-[10px] font-medium bg-amber-50 text-amber-700 border border-amber-100" title="非公開">🔒 </span>
<span className="px-1 rounded text-[10px] font-medium bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-100 dark:border-amber-500/30" title="非公開">🔒 </span>
)}
{task.visibility === 'org' && (
<span className="px-1 rounded text-[10px] font-medium bg-blue-50 text-blue-700 border border-blue-100" title={`${task.visibilityScopeOrgName ?? 'org'} と共有`}>
<span className="px-1 rounded text-[10px] font-medium bg-blue-50 dark:bg-blue-500/15 text-blue-700 dark:text-blue-300 border border-blue-100 dark:border-blue-500/30" title={`${task.visibilityScopeOrgName ?? 'org'} と共有`}>
🏢 {task.visibilityScopeOrgName ?? 'org'}
</span>
)}
{task.visibility === 'public' && (
<span className="px-1 rounded text-[10px] font-medium bg-emerald-50 text-emerald-700 border border-emerald-100" title="公開">🌐 </span>
<span className="px-1 rounded text-[10px] font-medium bg-emerald-50 dark:bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 border border-emerald-100 dark:border-emerald-500/30" title="公開">🌐 </span>
)}
</div>
)}
@ -665,7 +665,7 @@ function ScheduleDetailPane({
<button
type="button"
onClick={() => onDelete(task.id)}
className="px-2.5 sm:px-3 h-7 sm:h-8 rounded-md text-xs font-medium bg-canvas border border-red-200 text-red-700 hover:bg-red-50 active:scale-[0.97] active:bg-red-50 transition-[transform,background-color,color] duration-100 whitespace-nowrap"
className="px-2.5 sm:px-3 h-7 sm:h-8 rounded-md text-xs font-medium bg-canvas border border-red-200 text-red-700 dark:text-red-300 hover:bg-red-50 dark:hover:bg-red-500/15 active:scale-[0.97] active:bg-red-50 dark:active:bg-red-500/15 transition-[transform,background-color,color] duration-100 whitespace-nowrap"
>
</button>
@ -1210,7 +1210,7 @@ function ScheduleEditor({ mode, initialTask, onCancel, onSaved }: ScheduleEditor
<div className="text-2xs text-slate-500 mt-1"> {orgs[0].orgName}</div>
)}
{form.visibility === 'org' && orgs.length === 0 && (
<div className="text-2xs text-amber-700 mt-1">
<div className="text-2xs text-amber-700 dark:text-amber-300 mt-1">
Organization Private Public
</div>
)}
@ -1247,7 +1247,7 @@ function ScheduleEditor({ mode, initialTask, onCancel, onSaved }: ScheduleEditor
{error && (
<div
role="alert"
className="px-3.5 py-2.5 bg-red-50 border border-red-200 rounded-md text-xs text-red-700"
className="px-3.5 py-2.5 bg-red-50 dark:bg-red-500/15 border border-red-200 dark:border-red-500/30 rounded-md text-xs text-red-700 dark:text-red-300"
>
{error}
</div>

View File

@ -22,16 +22,16 @@ interface UserRecord {
type UserFilter = 'all' | 'admin' | 'user' | 'pending';
const STATUS_TONE: Record<UserRecord['status'], { bg: string; fg: string; border?: string; label: string }> = {
pending: { bg: 'bg-amber-50', fg: 'text-amber-700', border: 'border-amber-100', label: '承認待ち' },
active: { bg: 'bg-emerald-50', fg: 'text-emerald-700', border: 'border-emerald-100', label: 'アクティブ' },
pending: { bg: 'bg-amber-50 dark:bg-amber-500/15', fg: 'text-amber-700 dark:text-amber-300', border: 'border-amber-100 dark:border-amber-500/30', label: '承認待ち' },
active: { bg: 'bg-emerald-50 dark:bg-emerald-500/15', fg: 'text-emerald-700 dark:text-emerald-300', border: 'border-emerald-100 dark:border-emerald-500/30', label: 'アクティブ' },
disabled: { bg: 'bg-surface-2', fg: 'text-slate-600', border: 'border-hairline', label: '無効' },
};
const ROLE_TONE: Record<UserRecord['role'], { bg: string; fg: string; border?: string; label: string; desc: string }> = {
admin: {
bg: 'bg-blue-50',
fg: 'text-blue-700',
border: 'border-blue-100',
bg: 'bg-blue-50 dark:bg-blue-500/15',
fg: 'text-blue-700 dark:text-blue-300',
border: 'border-blue-100 dark:border-blue-500/30',
label: 'Admin',
desc: '全ての設定変更・ユーザー管理・システム操作',
},
@ -347,7 +347,7 @@ function UserListItem({ user, active, onClick }: { user: UserRecord; active: boo
{user.name || '(未設定)'}
</span>
{user.status === 'pending' && (
<span className="text-[10px] font-medium px-1 rounded bg-amber-50 text-amber-700 border border-amber-100 flex-shrink-0"></span>
<span className="text-[10px] font-medium px-1 rounded bg-amber-50 dark:bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-100 dark:border-amber-500/30 flex-shrink-0"></span>
)}
</div>
<div className="text-2xs text-slate-500 truncate">{user.email}</div>
@ -427,7 +427,7 @@ function UserDetailPane({ user, onPatch, onDelete, onMobileBack }: UserDetailPan
<button
type="button"
onClick={() => onPatch(user.id, { status: 'disabled' })}
className="px-3 h-7 rounded-md text-xs font-medium bg-canvas border border-amber-200 text-amber-700 hover:bg-amber-50 transition-colors whitespace-nowrap"
className="px-3 h-7 rounded-md text-xs font-medium bg-canvas border border-amber-200 text-amber-700 dark:text-amber-300 hover:bg-amber-50 dark:hover:bg-amber-500/15 transition-colors whitespace-nowrap"
>
</button>
@ -444,7 +444,7 @@ function UserDetailPane({ user, onPatch, onDelete, onMobileBack }: UserDetailPan
<button
type="button"
onClick={() => onDelete(user.id)}
className="px-3 h-7 rounded-md text-xs font-medium bg-canvas border border-red-200 text-red-700 hover:bg-red-50 transition-colors whitespace-nowrap"
className="px-3 h-7 rounded-md text-xs font-medium bg-canvas border border-red-200 text-red-700 dark:text-red-300 hover:bg-red-50 dark:hover:bg-red-500/15 transition-colors whitespace-nowrap"
>
</button>