@@ -217,6 +217,75 @@ function runUiTask<Args extends unknown[]>(
|
217 | 217 | }; |
218 | 218 | } |
219 | 219 | |
| 220 | +const SKILL_WORKSHOP_MODE_KEY = "openclaw:control-ui:skill-workshop-mode:v1"; |
| 221 | + |
| 222 | +export function loadSkillWorkshopMode(): "board" | "today" { |
| 223 | +try { |
| 224 | +const raw = getSafeLocalStorage()?.getItem(SKILL_WORKSHOP_MODE_KEY); |
| 225 | +return raw === "board" ? "board" : "today"; |
| 226 | +} catch { |
| 227 | +return "today"; |
| 228 | +} |
| 229 | +} |
| 230 | + |
| 231 | +function setSkillWorkshopMode(state: AppViewState, mode: "board" | "today"): void { |
| 232 | +if (state.skillWorkshopMode === mode) { |
| 233 | +return; |
| 234 | +} |
| 235 | +state.skillWorkshopMode = mode; |
| 236 | +try { |
| 237 | +getSafeLocalStorage()?.setItem(SKILL_WORKSHOP_MODE_KEY, mode); |
| 238 | +} catch { |
| 239 | +// Mode persistence is a convenience; the in-memory switch still works. |
| 240 | +} |
| 241 | +} |
| 242 | + |
| 243 | +function renderSkillWorkshopHeaderControls(state: AppViewState) { |
| 244 | +return html` |
| 245 | + <div class="sw-header-controls"> |
| 246 | + <div |
| 247 | + class="sw-mode-switch" |
| 248 | + role="tablist" |
| 249 | + aria-label="Workshop view" |
| 250 | + data-mode=${state.skillWorkshopMode} |
| 251 | + > |
| 252 | + <button |
| 253 | + type="button" |
| 254 | + class="sw-mode-switch__opt ${state.skillWorkshopMode === "board" ? "is-active" : ""}" |
| 255 | + role="tab" |
| 256 | + aria-selected=${state.skillWorkshopMode === "board" ? "true" : "false"} |
| 257 | + title="Board view" |
| 258 | + @click=${() => setSkillWorkshopMode(state, "board")} |
| 259 | + > |
| 260 | + <svg viewBox="0 0 24 24" class="sw-mode-switch__icon" aria-hidden="true"> |
| 261 | + <rect x="3" y="4" width="7" height="16" rx="1.5" /> |
| 262 | + <rect x="14" y="4" width="7" height="9" rx="1.5" /> |
| 263 | + <rect x="14" y="15" width="7" height="5" rx="1.5" /> |
| 264 | + </svg> |
| 265 | + <span>Board</span> |
| 266 | + </button> |
| 267 | + <button |
| 268 | + type="button" |
| 269 | + class="sw-mode-switch__opt ${state.skillWorkshopMode === "today" ? "is-active" : ""}" |
| 270 | + role="tab" |
| 271 | + aria-selected=${state.skillWorkshopMode === "today" ? "true" : "false"} |
| 272 | + title="Today view" |
| 273 | + @click=${() => setSkillWorkshopMode(state, "today")} |
| 274 | + > |
| 275 | + <svg viewBox="0 0 24 24" class="sw-mode-switch__icon" aria-hidden="true"> |
| 276 | + <circle cx="12" cy="12" r="4" /> |
| 277 | + <path |
| 278 | + d="M12 3v2M12 19v2M3 12h2M19 12h2M5.6 5.6l1.4 1.4M17 17l1.4 1.4M5.6 18.4 7 17M17 7l1.4-1.4" |
| 279 | + /> |
| 280 | + </svg> |
| 281 | + <span>Today</span> |
| 282 | + </button> |
| 283 | + <span class="sw-mode-switch__indicator" aria-hidden="true"></span> |
| 284 | + </div> |
| 285 | + </div> |
| 286 | + `; |
| 287 | +} |
| 288 | + |
220 | 289 | function renderSettingsSectionNav(state: AppViewState) { |
221 | 290 | if (!isSettingsTab(state.tab)) { |
222 | 291 | return nothing; |
@@ -2246,6 +2315,9 @@ export function renderApp(state: AppViewState) {
|
2246 | 2315 | <div class="page-sub">${subtitleForTab(state.tab)}</div> |
2247 | 2316 | </div> |
2248 | 2317 | <div class="page-meta"> |
| 2318 | + ${state.tab === "skillWorkshop" |
| 2319 | + ? renderSkillWorkshopHeaderControls(state) |
| 2320 | + : nothing} |
2249 | 2321 | ${state.tab === "dreams" |
2250 | 2322 | ? html` |
2251 | 2323 | <div class="dreaming-header-controls"> |
@@ -3150,7 +3222,7 @@ export function renderApp(state: AppViewState) {
|
3150 | 3222 | }, |
3151 | 3223 | onFilePreviewQueryChange: (query) => (state.skillWorkshopFilePreviewQuery = query), |
3152 | 3224 | onQueueWidthChange: (width) => (state.skillWorkshopQueueWidth = width), |
3153 | | - onModeChange: (mode) => (state.skillWorkshopMode = mode), |
| 3225 | + onModeChange: (mode) => setSkillWorkshopMode(state, mode), |
3154 | 3226 | onSelect: (key) => { |
3155 | 3227 | state.skillWorkshopFilePreviewKey = null; |
3156 | 3228 | selectSkillWorkshopProposal(state, key); |
|