|
126 | 126 | return "opencode"; |
127 | 127 | } |
128 | 128 |
|
| 129 | + function shouldShowChannelModel(channel: { agentProvider?: string }): boolean { |
| 130 | + return getChannelProvider(channel) === "opencode"; |
| 131 | + } |
| 132 | +
|
129 | 133 | function setChannelProvider(workspaceId: string, channelId: string, provider: AgentProvider): void { |
130 | 134 | config = { |
131 | 135 | ...config, |
|
273 | 277 | }); |
274 | 278 | </script> |
275 | 279 |
|
276 | | -<main class="page"> |
277 | | - <header class="header"> |
278 | | - <h1>Ode Settings</h1> |
279 | | - <ThemeToggle /> |
280 | | - </header> |
| 280 | +<main> |
| 281 | + <div class="container"> |
| 282 | + <nav class="navbar"> |
| 283 | + <div class="navbar-spacer"></div> |
| 284 | + <div class="navbar-title">Ode Setting</div> |
| 285 | + <div class="navbar-actions"> |
| 286 | + <ThemeToggle /> |
| 287 | + </div> |
| 288 | + </nav> |
281 | 289 |
|
282 | | - <div class="layout"> |
| 290 | + <div class="layout"> |
283 | 291 | <aside class="sidebar card"> |
284 | 292 | <button class="nav-item {activeSection === 'profile' ? 'active' : ''}" on:click={() => goto('/local-setting/profile')}> |
285 | 293 | Profile |
|
350 | 358 | </div> |
351 | 359 | </section> |
352 | 360 | {:else if activeSection === "agent"} |
353 | | - <section class="card"> |
354 | | - <div class="card-head"> |
| 361 | + <section class="card agent-card"> |
| 362 | + <div class="card-head agent-head"> |
355 | 363 | <h2>Agent</h2> |
356 | | - <button on:click={checkAgents} disabled={isCheckingCli || isLoading || isSaving}> |
| 364 | + <button class="btn-sync" on:click={checkAgents} disabled={isCheckingCli || isLoading || isSaving}> |
357 | 365 | {isCheckingCli ? "Checking..." : "Check"} |
358 | 366 | </button> |
359 | 367 | </div> |
360 | 368 |
|
361 | | - <div class="agent-row"> |
362 | | - <strong>OpenCode CLI</strong> |
363 | | - <span class="badge {config.agents.opencode.enabled ? 'on' : 'off'}"> |
364 | | - {config.agents.opencode.enabled ? "Enabled" : "Disabled"} |
365 | | - </span> |
366 | | - {#if cliCheckResult} |
367 | | - <span class="check-result {cliCheckResult.opencode ? 'ok' : 'bad'}"> |
368 | | - {cliCheckResult.opencode ? "Installed" : "Not found"} |
| 369 | + <div class="agent-status-grid"> |
| 370 | + <div class="agent-row"> |
| 371 | + <strong>OpenCode CLI</strong> |
| 372 | + <span class="badge {config.agents.opencode.enabled ? 'on' : 'off'}"> |
| 373 | + {config.agents.opencode.enabled ? "Enabled" : "Disabled"} |
369 | 374 | </span> |
370 | | - {/if} |
371 | | - </div> |
| 375 | + {#if cliCheckResult} |
| 376 | + <span class="check-result {cliCheckResult.opencode ? 'ok' : 'bad'}"> |
| 377 | + {cliCheckResult.opencode ? "Installed" : "Not found"} |
| 378 | + </span> |
| 379 | + {/if} |
| 380 | + </div> |
372 | 381 |
|
373 | | - <div class="agent-row"> |
374 | | - <strong>Claude CLI</strong> |
375 | | - <span class="badge {config.agents.claudecode.enabled ? 'on' : 'off'}"> |
376 | | - {config.agents.claudecode.enabled ? "Enabled" : "Disabled"} |
377 | | - </span> |
378 | | - {#if cliCheckResult} |
379 | | - <span class="check-result {cliCheckResult.claude ? 'ok' : 'bad'}"> |
380 | | - {cliCheckResult.claude ? "Installed" : "Not found"} |
| 382 | + <div class="agent-row"> |
| 383 | + <strong>Claude CLI</strong> |
| 384 | + <span class="badge {config.agents.claudecode.enabled ? 'on' : 'off'}"> |
| 385 | + {config.agents.claudecode.enabled ? "Enabled" : "Disabled"} |
381 | 386 | </span> |
382 | | - {/if} |
| 387 | + {#if cliCheckResult} |
| 388 | + <span class="check-result {cliCheckResult.claude ? 'ok' : 'bad'}"> |
| 389 | + {cliCheckResult.claude ? "Installed" : "Not found"} |
| 390 | + </span> |
| 391 | + {/if} |
| 392 | + </div> |
383 | 393 | </div> |
384 | 394 |
|
385 | | - <label for="agent-opencode-models">OpenCode models (one per line)</label> |
386 | | - <textarea id="agent-opencode-models" rows="8" bind:value={opencodeModelsText}></textarea> |
| 395 | + <div class="models-section"> |
| 396 | + <div class="section-header"> |
| 397 | + <h3>OpenCode Models</h3> |
| 398 | + <span class="hint-text">One model per line</span> |
| 399 | + </div> |
| 400 | + <textarea id="agent-opencode-models" rows="8" bind:value={opencodeModelsText}></textarea> |
| 401 | + </div> |
387 | 402 | </section> |
388 | 403 | {:else} |
389 | 404 | <section class="card"> |
|
426 | 441 | {/each} |
427 | 442 | </select> |
428 | 443 |
|
429 | | - {#if channel.agentProvider === "opencode"} |
| 444 | + {#if shouldShowChannelModel(channel)} |
430 | 445 | <label for={`channel-model-${channel.id}`}>Model</label> |
431 | 446 | <input id={`channel-model-${channel.id}`} bind:value={channel.model} placeholder="openai/gpt-5.3-codex" /> |
432 | 447 | {/if} |
|
455 | 470 | <p class="message">{message}</p> |
456 | 471 | {/if} |
457 | 472 | </section> |
| 473 | + </div> |
458 | 474 | </div> |
459 | 475 | </main> |
460 | 476 |
|
461 | 477 | <style> |
462 | | - .page { |
463 | | - max-width: 1160px; |
| 478 | + :global(body) { |
| 479 | + background: var(--bg); |
| 480 | + } |
| 481 | +
|
| 482 | + .container { |
| 483 | + width: 100%; |
| 484 | + max-width: 1000px; |
464 | 485 | margin: 0 auto; |
465 | 486 | padding: 24px; |
| 487 | + box-sizing: border-box; |
466 | 488 | } |
467 | 489 |
|
468 | | - .header { |
469 | | - display: flex; |
| 490 | + .navbar { |
| 491 | + height: 64px; |
| 492 | + border: 1px solid var(--line); |
| 493 | + display: grid; |
| 494 | + grid-template-columns: 1fr auto 1fr; |
470 | 495 | align-items: center; |
471 | | - justify-content: space-between; |
| 496 | + padding: 0 24px; |
| 497 | + background: var(--card); |
| 498 | + border-radius: 8px; |
472 | 499 | margin-bottom: 16px; |
473 | 500 | } |
474 | 501 |
|
| 502 | + .navbar-title { |
| 503 | + font-size: 18px; |
| 504 | + font-weight: 600; |
| 505 | + color: var(--ink); |
| 506 | + text-align: center; |
| 507 | + } |
| 508 | +
|
| 509 | + .navbar-actions { |
| 510 | + display: flex; |
| 511 | + justify-content: flex-end; |
| 512 | + } |
| 513 | +
|
475 | 514 | .layout { |
476 | 515 | display: grid; |
477 | 516 | grid-template-columns: 260px minmax(0, 1fr); |
|
573 | 612 | flex-wrap: wrap; |
574 | 613 | } |
575 | 614 |
|
| 615 | + .agent-card { |
| 616 | + gap: 14px; |
| 617 | + } |
| 618 | +
|
| 619 | + .agent-head { |
| 620 | + margin-bottom: 2px; |
| 621 | + } |
| 622 | +
|
| 623 | + .agent-status-grid { |
| 624 | + display: grid; |
| 625 | + gap: 10px; |
| 626 | + } |
| 627 | +
|
| 628 | + .models-section { |
| 629 | + border: 1px solid var(--line); |
| 630 | + border-radius: 10px; |
| 631 | + padding: 12px; |
| 632 | + background: var(--bg-soft); |
| 633 | + display: grid; |
| 634 | + gap: 8px; |
| 635 | + } |
| 636 | +
|
| 637 | + .section-header { |
| 638 | + display: flex; |
| 639 | + align-items: center; |
| 640 | + justify-content: space-between; |
| 641 | + gap: 8px; |
| 642 | + } |
| 643 | +
|
| 644 | + .hint-text { |
| 645 | + font-size: 12px; |
| 646 | + color: var(--ink-soft); |
| 647 | + } |
| 648 | +
|
| 649 | + .btn-sync { |
| 650 | + background: var(--bg-soft); |
| 651 | + border: 1px solid var(--line); |
| 652 | + padding: 6px 12px; |
| 653 | + border-radius: 6px; |
| 654 | + font-size: 12px; |
| 655 | + font-weight: 600; |
| 656 | + min-width: auto; |
| 657 | + } |
| 658 | +
|
| 659 | + .btn-sync:disabled { |
| 660 | + opacity: 0.6; |
| 661 | + cursor: not-allowed; |
| 662 | + } |
| 663 | +
|
576 | 664 | .badge { |
577 | 665 | padding: 2px 8px; |
578 | 666 | border-radius: 999px; |
|
648 | 736 | } |
649 | 737 |
|
650 | 738 | @media (max-width: 768px) { |
651 | | - .page { |
| 739 | + .container { |
652 | 740 | padding: 16px; |
653 | 741 | } |
| 742 | +
|
| 743 | + .navbar { |
| 744 | + padding: 0 12px; |
| 745 | + } |
| 746 | +
|
| 747 | + .navbar-title { |
| 748 | + font-size: 16px; |
| 749 | + } |
654 | 750 | } |
655 | 751 | </style> |
0 commit comments