Skip to content

Commit 5156fd8

Browse files
committed
feat(composer): add plan mode toggle and refresh meta bar icons
1 parent 940fd61 commit 5156fd8

2 files changed

Lines changed: 151 additions & 60 deletions

File tree

src/features/composer/components/ComposerMetaBar.tsx

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { CSSProperties } from "react";
2+
import { BrainCog } from "lucide-react";
23
import type { AccessMode, ThreadTokenUsage } from "../../../types";
34

45
type ComposerMetaBarProps = {
@@ -46,61 +47,109 @@ export function ComposerMetaBar({
4647
Math.min(Math.max((usedTokens / contextWindow) * 100, 0), 100),
4748
)
4849
: null;
50+
const planMode =
51+
collaborationModes.find((mode) => mode.id === "plan") ?? null;
52+
const defaultMode =
53+
collaborationModes.find((mode) => mode.id === "default") ?? null;
54+
const canUsePlanToggle =
55+
Boolean(planMode) &&
56+
collaborationModes.every(
57+
(mode) => mode.id === "default" || mode.id === "plan",
58+
);
59+
const planSelected = selectedCollaborationModeId === (planMode?.id ?? "");
4960

5061
return (
5162
<div className="composer-bar">
5263
<div className="composer-meta">
5364
{collaborationModes.length > 0 && (
54-
<div className="composer-select-wrap">
65+
canUsePlanToggle ? (
66+
<div className="composer-select-wrap composer-plan-toggle-wrap">
67+
<label className="composer-plan-toggle" aria-label="Plan mode">
68+
<input
69+
className="composer-plan-toggle-input"
70+
type="checkbox"
71+
checked={planSelected}
72+
disabled={disabled}
73+
onChange={(event) =>
74+
onSelectCollaborationMode(
75+
event.target.checked
76+
? planMode?.id ?? "plan"
77+
: (defaultMode?.id ?? null),
78+
)
79+
}
80+
/>
81+
<span className="composer-plan-toggle-icon" aria-hidden>
82+
<svg viewBox="0 0 24 24" fill="none">
83+
<path
84+
d="m6.5 7.5 1 1 2-2M6.5 12.5l1 1 2-2M6.5 17.5l1 1 2-2M11 7.5h7M11 12.5h7M11 17.5h7"
85+
stroke="currentColor"
86+
strokeWidth="1.4"
87+
strokeLinecap="round"
88+
strokeLinejoin="round"
89+
/>
90+
</svg>
91+
</span>
92+
<span className="composer-plan-toggle-label">
93+
{planMode?.label || "Plan"}
94+
</span>
95+
</label>
96+
</div>
97+
) : (
98+
<div className="composer-select-wrap">
5599
<span className="composer-icon" aria-hidden>
56100
<svg viewBox="0 0 24 24" fill="none">
57101
<path
58-
d="M7 7h10M7 12h6M7 17h8"
102+
d="m6.5 7.5 1 1 2-2M6.5 12.5l1 1 2-2M6.5 17.5l1 1 2-2M11 7.5h7M11 12.5h7M11 17.5h7"
59103
stroke="currentColor"
60104
strokeWidth="1.4"
61105
strokeLinecap="round"
106+
strokeLinejoin="round"
62107
/>
63108
</svg>
64109
</span>
65-
<select
66-
className="composer-select composer-select--model composer-select--collab"
67-
aria-label="Collaboration mode"
68-
value={selectedCollaborationModeId ?? ""}
69-
onChange={(event) =>
70-
onSelectCollaborationMode(event.target.value || null)
71-
}
72-
disabled={disabled}
73-
>
74-
{collaborationModes.map((mode) => (
75-
<option key={mode.id} value={mode.id}>
76-
{mode.label || mode.id}
77-
</option>
78-
))}
79-
</select>
80-
</div>
110+
<select
111+
className="composer-select composer-select--model composer-select--collab"
112+
aria-label="Collaboration mode"
113+
value={selectedCollaborationModeId ?? ""}
114+
onChange={(event) =>
115+
onSelectCollaborationMode(event.target.value || null)
116+
}
117+
disabled={disabled}
118+
>
119+
{collaborationModes.map((mode) => (
120+
<option key={mode.id} value={mode.id}>
121+
{mode.label || mode.id}
122+
</option>
123+
))}
124+
</select>
125+
</div>
126+
)
81127
)}
82-
<div className="composer-select-wrap">
83-
<span className="composer-icon" aria-hidden>
128+
<div className="composer-select-wrap composer-select-wrap--model">
129+
<span className="composer-icon composer-icon--model" aria-hidden>
84130
<svg viewBox="0 0 24 24" fill="none">
85131
<path
86-
d="M7 8V6a5 5 0 0 1 10 0v2"
132+
d="M12 4v2"
87133
stroke="currentColor"
88134
strokeWidth="1.4"
89135
strokeLinecap="round"
90136
/>
91-
<rect
92-
x="4.5"
93-
y="8"
94-
width="15"
95-
height="11"
96-
rx="3"
137+
<path
138+
d="M8 7.5h8a2.5 2.5 0 0 1 2.5 2.5v5a2.5 2.5 0 0 1-2.5 2.5H8A2.5 2.5 0 0 1 5.5 15v-5A2.5 2.5 0 0 1 8 7.5Z"
97139
stroke="currentColor"
98140
strokeWidth="1.4"
141+
strokeLinejoin="round"
99142
/>
100-
<circle cx="9" cy="13" r="1" fill="currentColor" />
101-
<circle cx="15" cy="13" r="1" fill="currentColor" />
143+
<circle cx="9.5" cy="12.5" r="1" fill="currentColor" />
144+
<circle cx="14.5" cy="12.5" r="1" fill="currentColor" />
102145
<path
103-
d="M9 16h6"
146+
d="M9.5 15.5h5"
147+
stroke="currentColor"
148+
strokeWidth="1.4"
149+
strokeLinecap="round"
150+
/>
151+
<path
152+
d="M5.5 11H4M20 11h-1.5"
104153
stroke="currentColor"
105154
strokeWidth="1.4"
106155
strokeLinecap="round"
@@ -122,34 +171,9 @@ export function ComposerMetaBar({
122171
))}
123172
</select>
124173
</div>
125-
<div className="composer-select-wrap">
126-
<span className="composer-icon" aria-hidden>
127-
<svg viewBox="0 0 24 24" fill="none">
128-
<path
129-
d="M8.5 4.5a3.5 3.5 0 0 0-3.46 4.03A4 4 0 0 0 6 16.5h2"
130-
stroke="currentColor"
131-
strokeWidth="1.4"
132-
strokeLinecap="round"
133-
/>
134-
<path
135-
d="M15.5 4.5a3.5 3.5 0 0 1 3.46 4.03A4 4 0 0 1 18 16.5h-2"
136-
stroke="currentColor"
137-
strokeWidth="1.4"
138-
strokeLinecap="round"
139-
/>
140-
<path
141-
d="M9 12h6"
142-
stroke="currentColor"
143-
strokeWidth="1.4"
144-
strokeLinecap="round"
145-
/>
146-
<path
147-
d="M12 12v6"
148-
stroke="currentColor"
149-
strokeWidth="1.4"
150-
strokeLinecap="round"
151-
/>
152-
</svg>
174+
<div className="composer-select-wrap composer-select-wrap--effort">
175+
<span className="composer-icon composer-icon--effort" aria-hidden>
176+
<BrainCog size={14} strokeWidth={1.8} />
153177
</span>
154178
<select
155179
className="composer-select composer-select--effort"

src/styles/composer.css

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,58 @@
645645
width: max-content;
646646
}
647647

648+
.composer-plan-toggle-wrap {
649+
padding: 2px 8px;
650+
}
651+
652+
.composer-plan-toggle {
653+
display: inline-flex;
654+
align-items: center;
655+
gap: 6px;
656+
cursor: pointer;
657+
user-select: none;
658+
}
659+
660+
.composer-plan-toggle-input {
661+
margin: 0;
662+
width: 12px;
663+
height: 12px;
664+
accent-color: var(--message-link-color);
665+
cursor: pointer;
666+
}
667+
668+
.composer-plan-toggle-icon {
669+
display: inline-flex;
670+
width: 16px;
671+
height: 16px;
672+
color: var(--text-muted);
673+
}
674+
675+
.composer-plan-toggle-icon svg {
676+
width: 16px;
677+
height: 16px;
678+
}
679+
680+
.composer-plan-toggle-label {
681+
font-size: 11px;
682+
color: var(--text-muted);
683+
line-height: 1;
684+
}
685+
686+
.composer-plan-toggle:has(.composer-plan-toggle-input:checked) .composer-plan-toggle-icon,
687+
.composer-plan-toggle:has(.composer-plan-toggle-input:checked) .composer-plan-toggle-label {
688+
color: var(--text-strong);
689+
}
690+
691+
.composer-plan-toggle-input:disabled {
692+
cursor: not-allowed;
693+
}
694+
695+
.composer-plan-toggle:has(.composer-plan-toggle-input:disabled) .composer-plan-toggle-icon,
696+
.composer-plan-toggle:has(.composer-plan-toggle-input:disabled) .composer-plan-toggle-label {
697+
opacity: 0.6;
698+
}
699+
648700
.composer-icon {
649701
display: inline-flex;
650702
width: 14px;
@@ -657,6 +709,21 @@
657709
height: 14px;
658710
}
659711

712+
.composer-icon--model {
713+
width: 16px;
714+
height: 16px;
715+
}
716+
717+
.composer-icon--model svg {
718+
width: 16px;
719+
height: 16px;
720+
}
721+
722+
.composer-icon--model,
723+
.composer-icon--effort {
724+
transform: translateY(1px);
725+
}
726+
660727
.composer-select {
661728
appearance: none;
662729
border: none;
@@ -684,8 +751,8 @@
684751
}
685752

686753
.composer-select--collab {
687-
width: 110px;
688-
max-width: 110px;
754+
width: 78px;
755+
max-width: 78px;
689756
}
690757

691758
.composer-select--effort {

0 commit comments

Comments
 (0)