Skip to content

Commit 8b2a232

Browse files
authored
Merge pull request #41 from odefun/fix/restore-agent-style-only
fix: restore agent panel styling without moving navbar
2 parents a30e64a + f1da653 commit 8b2a232

File tree

4 files changed

+159
-47
lines changed

4 files changed

+159
-47
lines changed

packages/core/web/server.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ function jsonResponse(status: number, payload: JsonResponse): Response {
6464
}
6565

6666
function resolveAssetPath(pathname: string): string {
67+
const appAssetIndex = pathname.indexOf("/_app/");
68+
if (appAssetIndex >= 0) {
69+
return pathname.slice(appAssetIndex);
70+
}
6771
if (pathname === "/") return "/index.html";
6872
if (pathname === "/local-setting") return "/local-setting.html";
6973
if (pathname.startsWith("/local-setting/")) return "/local-setting.html";

packages/ims/slack/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ async function postSettingsLauncher(
242242
type: "section",
243243
text: {
244244
type: "mrkdwn",
245-
text: "Open channel settings for agent, model (OpenCode), and working directory.",
245+
text: "Open channel settings for agent and working directory (model appears only for OpenCode).",
246246
},
247247
},
248248
{

packages/ims/slack/commands.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const MODEL_ACTION = "model_select";
3131
const WORKING_DIR_BLOCK = "working_dir";
3232
const WORKING_DIR_ACTION = "working_dir_input";
3333

34-
type AgentProvider = "opencode" | "claudecode";
34+
type AgentProvider = "opencode" | "claudecode" | "codex";
3535

3636
function normalizeModel(value: string): string {
3737
return value.trim().toLowerCase();
@@ -45,14 +45,17 @@ function findMatchingModel(models: string[], value: string | null | undefined):
4545

4646
function getSelectableProviders(): AgentProvider[] {
4747
const enabled = getEnabledAgentProviders().filter(
48-
(provider): provider is AgentProvider => provider === "opencode" || provider === "claudecode"
48+
(provider): provider is AgentProvider =>
49+
provider === "opencode" || provider === "claudecode" || provider === "codex"
4950
);
5051
if (enabled.length > 0) return enabled;
51-
return ["opencode", "claudecode"];
52+
return ["opencode", "claudecode", "codex"];
5253
}
5354

5455
function toSelectableProvider(provider: "opencode" | "claudecode" | "codex"): AgentProvider {
55-
return provider === "claudecode" ? "claudecode" : "opencode";
56+
if (provider === "claudecode") return "claudecode";
57+
if (provider === "codex") return "codex";
58+
return "opencode";
5659
}
5760

5861
function buildSettingsModal(params: {
@@ -74,6 +77,7 @@ function buildSettingsModal(params: {
7477
const providerLabels: Record<AgentProvider, string> = {
7578
opencode: "OpenCode",
7679
claudecode: "Claude Code",
80+
codex: "Codex",
7781
};
7882
const providerOptions = enabledProviders.map((provider) => ({
7983
text: { type: "plain_text" as const, text: providerLabels[provider] },
@@ -90,13 +94,16 @@ function buildSettingsModal(params: {
9094
const initialModel = matchedSelectedModel
9195
? matchedSelectedModel
9296
: (opencodeModels[0] ?? "__none__");
97+
const introText = selectedProvider === "opencode"
98+
? "Configure agent, model (OpenCode), and working directory for this channel."
99+
: "Configure agent and working directory for this channel.";
93100

94101
const blocks: any[] = [
95102
{
96103
type: "section" as const,
97104
text: {
98105
type: "mrkdwn" as const,
99-
text: "Configure agent, model (OpenCode), and working directory for this channel.",
106+
text: introText,
100107
},
101108
},
102109
{
@@ -279,9 +286,12 @@ export function setupInteractiveHandlers(): void {
279286
if (!view) return;
280287

281288
const channelId = view.private_metadata;
282-
const selectedProvider = (body as any).actions?.[0]?.selected_option?.value === "claudecode"
289+
const selectedOption = (body as any).actions?.[0]?.selected_option?.value;
290+
const selectedProvider = selectedOption === "claudecode"
283291
? "claudecode"
284-
: "opencode";
292+
: selectedOption === "codex"
293+
? "codex"
294+
: "opencode";
285295
if (selectedProvider === "opencode") {
286296
try {
287297
await startOpenCodeServer();
@@ -316,7 +326,9 @@ export function setupInteractiveHandlers(): void {
316326
const selectedProvider =
317327
values?.[PROVIDER_BLOCK]?.[PROVIDER_ACTION]?.selected_option?.value === "claudecode"
318328
? "claudecode"
319-
: "opencode";
329+
: values?.[PROVIDER_BLOCK]?.[PROVIDER_ACTION]?.selected_option?.value === "codex"
330+
? "codex"
331+
: "opencode";
320332
const selectedModel = values?.[MODEL_BLOCK]?.[MODEL_ACTION]?.selected_option?.value;
321333
const workingDirectory = values?.[WORKING_DIR_BLOCK]?.[WORKING_DIR_ACTION]?.value || "";
322334

@@ -349,7 +361,7 @@ export function setupInteractiveHandlers(): void {
349361
const normalizedSelectedModel = findMatchingModel(getOpenCodeModels(), selectedModel) ?? selectedModel;
350362
setChannelModel(channelId, normalizedSelectedModel);
351363
}
352-
if (selectedProvider === "claudecode") {
364+
if (selectedProvider !== "opencode") {
353365
setChannelModel(channelId, "");
354366
}
355367

packages/web-ui/src/routes/local-setting/+page.svelte

Lines changed: 133 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@
126126
return "opencode";
127127
}
128128
129+
function shouldShowChannelModel(channel: { agentProvider?: string }): boolean {
130+
return getChannelProvider(channel) === "opencode";
131+
}
132+
129133
function setChannelProvider(workspaceId: string, channelId: string, provider: AgentProvider): void {
130134
config = {
131135
...config,
@@ -273,13 +277,17 @@
273277
});
274278
</script>
275279

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>
281289

282-
<div class="layout">
290+
<div class="layout">
283291
<aside class="sidebar card">
284292
<button class="nav-item {activeSection === 'profile' ? 'active' : ''}" on:click={() => goto('/local-setting/profile')}>
285293
Profile
@@ -350,40 +358,47 @@
350358
</div>
351359
</section>
352360
{: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">
355363
<h2>Agent</h2>
356-
<button on:click={checkAgents} disabled={isCheckingCli || isLoading || isSaving}>
364+
<button class="btn-sync" on:click={checkAgents} disabled={isCheckingCli || isLoading || isSaving}>
357365
{isCheckingCli ? "Checking..." : "Check"}
358366
</button>
359367
</div>
360368

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"}
369374
</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>
372381

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"}
381386
</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>
383393
</div>
384394

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>
387402
</section>
388403
{:else}
389404
<section class="card">
@@ -426,7 +441,7 @@
426441
{/each}
427442
</select>
428443

429-
{#if channel.agentProvider === "opencode"}
444+
{#if shouldShowChannelModel(channel)}
430445
<label for={`channel-model-${channel.id}`}>Model</label>
431446
<input id={`channel-model-${channel.id}`} bind:value={channel.model} placeholder="openai/gpt-5.3-codex" />
432447
{/if}
@@ -455,23 +470,47 @@
455470
<p class="message">{message}</p>
456471
{/if}
457472
</section>
473+
</div>
458474
</div>
459475
</main>
460476

461477
<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;
464485
margin: 0 auto;
465486
padding: 24px;
487+
box-sizing: border-box;
466488
}
467489
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;
470495
align-items: center;
471-
justify-content: space-between;
496+
padding: 0 24px;
497+
background: var(--card);
498+
border-radius: 8px;
472499
margin-bottom: 16px;
473500
}
474501
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+
475514
.layout {
476515
display: grid;
477516
grid-template-columns: 260px minmax(0, 1fr);
@@ -573,6 +612,55 @@
573612
flex-wrap: wrap;
574613
}
575614
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+
576664
.badge {
577665
padding: 2px 8px;
578666
border-radius: 999px;
@@ -648,8 +736,16 @@
648736
}
649737
650738
@media (max-width: 768px) {
651-
.page {
739+
.container {
652740
padding: 16px;
653741
}
742+
743+
.navbar {
744+
padding: 0 12px;
745+
}
746+
747+
.navbar-title {
748+
font-size: 16px;
749+
}
654750
}
655751
</style>

0 commit comments

Comments
 (0)