mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-04 22:15:07 +03:00
Compare commits
1 Commits
fix/releas
...
fix/releas
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd426a0d84 |
@@ -1 +0,0 @@
|
||||
- **fix(dashboard):** The Combos page usage guide now reads its dismissal through `useSyncExternalStore` instead of correcting SSR state inside an effect, removing an extra commit of the page tree on every load (and the `react-hooks/set-state-in-effect` error it raised).
|
||||
@@ -1,15 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
useState,
|
||||
useEffect,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
useSyncExternalStore,
|
||||
memo,
|
||||
Suspense,
|
||||
} from "react";
|
||||
import { useState, useEffect, useCallback, useMemo, useRef, memo, Suspense } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
import Link from "next/link";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
@@ -397,42 +388,6 @@ const STRATEGY_RECOMMENDATIONS_FALLBACK = {
|
||||
|
||||
const COMBO_USAGE_GUIDE_STORAGE_KEY = "omniroute:combos:hide-usage-guide";
|
||||
|
||||
// The dismissal lives in localStorage, which SSR cannot read: a lazy useState
|
||||
// initializer would render "not dismissed" on the server and the real value on
|
||||
// the client, and correcting that in an effect is a synchronous setState inside
|
||||
// an effect (react-hooks/set-state-in-effect) that costs an extra commit of this
|
||||
// whole tree. useSyncExternalStore is the sanctioned shape for exactly this —
|
||||
// getServerSnapshot supplies the SSR-safe default, getSnapshot reads the store
|
||||
// after hydration, and the two handlers below notify subscribers instead of
|
||||
// setting state. The `storage` listener keeps other tabs in sync for free.
|
||||
const usageGuideListeners = new Set<() => void>();
|
||||
|
||||
function subscribeUsageGuide(onStoreChange: () => void): () => void {
|
||||
usageGuideListeners.add(onStoreChange);
|
||||
globalThis.addEventListener?.("storage", onStoreChange);
|
||||
return () => {
|
||||
usageGuideListeners.delete(onStoreChange);
|
||||
globalThis.removeEventListener?.("storage", onStoreChange);
|
||||
};
|
||||
}
|
||||
|
||||
function emitUsageGuideChange(): void {
|
||||
for (const listener of usageGuideListeners) listener();
|
||||
}
|
||||
|
||||
function getUsageGuideSnapshot(): boolean {
|
||||
try {
|
||||
return globalThis.localStorage?.getItem(COMBO_USAGE_GUIDE_STORAGE_KEY) !== "1";
|
||||
} catch {
|
||||
// Storage access errors (privacy mode / restricted environments) show the guide.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function getUsageGuideServerSnapshot(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pure predicate hoisted out of the page component to keep its cyclomatic budget flat
|
||||
// (check:complexity new-code mode).
|
||||
function isStaleIntelligentSelection(
|
||||
@@ -813,15 +768,14 @@ function CombosPageContent() {
|
||||
// full client-only re-render of this tree, discarding whatever the fetch
|
||||
// effects below had already populated. Start with the SSR-safe default on
|
||||
// both passes and correct it client-only, after hydration, in an effect.
|
||||
const usageGuideNotDismissed = useSyncExternalStore(
|
||||
subscribeUsageGuide,
|
||||
getUsageGuideSnapshot,
|
||||
getUsageGuideServerSnapshot
|
||||
);
|
||||
// "Hide" (as opposed to "hide forever") is intentionally per-mount: it is not
|
||||
// persisted, and remounting the page brings the guide back — same as before.
|
||||
const [usageGuideHiddenForNow, setUsageGuideHiddenForNow] = useState(false);
|
||||
const showUsageGuide = usageGuideNotDismissed && !usageGuideHiddenForNow;
|
||||
const [showUsageGuide, setShowUsageGuide] = useState(true);
|
||||
useEffect(() => {
|
||||
try {
|
||||
setShowUsageGuide(globalThis.localStorage?.getItem(COMBO_USAGE_GUIDE_STORAGE_KEY) !== "1");
|
||||
} catch {
|
||||
// Ignore storage access errors (privacy mode / restricted environments)
|
||||
}
|
||||
}, []);
|
||||
const [recentlyCreatedCombo, setRecentlyCreatedCombo] = useState("");
|
||||
const [creatingKimiPreset, setCreatingKimiPreset] = useState(false);
|
||||
const [comboDragIndex, setComboDragIndex] = useState(null);
|
||||
@@ -1052,18 +1006,17 @@ function CombosPageContent() {
|
||||
};
|
||||
|
||||
const handleHideUsageGuideForever = () => {
|
||||
setShowUsageGuide(false);
|
||||
try {
|
||||
globalThis.localStorage?.setItem(COMBO_USAGE_GUIDE_STORAGE_KEY, "1");
|
||||
} catch {}
|
||||
emitUsageGuideChange();
|
||||
};
|
||||
|
||||
const handleShowUsageGuide = () => {
|
||||
setShowUsageGuide(true);
|
||||
try {
|
||||
globalThis.localStorage?.removeItem(COMBO_USAGE_GUIDE_STORAGE_KEY);
|
||||
} catch {}
|
||||
setUsageGuideHiddenForNow(false);
|
||||
emitUsageGuideChange();
|
||||
};
|
||||
|
||||
const handleFilterChange = (nextFilter) => {
|
||||
@@ -1196,7 +1149,7 @@ function CombosPageContent() {
|
||||
|
||||
{showUsageGuide && (
|
||||
<ComboUsageGuide
|
||||
onHide={() => setUsageGuideHiddenForNow(true)}
|
||||
onHide={() => setShowUsageGuide(false)}
|
||||
onHideForever={handleHideUsageGuideForever}
|
||||
onCreateCombo={() => setShowCreateModal(true)}
|
||||
/>
|
||||
|
||||
@@ -170,7 +170,7 @@ function buildOpenAIToolCallResponse({
|
||||
);
|
||||
}
|
||||
|
||||
function buildClaudeResponse(text = "ok", model = "claude-3-5-sonnet-20241022") {
|
||||
function buildClaudeResponse(text = "ok", model = "claude-sonnet-4-6") {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
id: "msg_json",
|
||||
@@ -286,7 +286,7 @@ function buildOpenAIStreamResponse(text = "streamed from openai") {
|
||||
|
||||
function buildOpenAIResponsesSSE({
|
||||
text = "responses streamed from codex",
|
||||
model = "gpt-5.1-codex",
|
||||
model = "gpt-5.6-sol",
|
||||
usage = null,
|
||||
} = {}) {
|
||||
return new Response(
|
||||
@@ -567,7 +567,7 @@ test("chat pipeline persists Codex responses cache and reasoning tokens to call
|
||||
buildRequest({
|
||||
url: "http://localhost/v1/responses",
|
||||
body: {
|
||||
model: "codex/gpt-5.1-codex",
|
||||
model: "codex/gpt-5.6-sol",
|
||||
stream: false,
|
||||
input: "Persist cache + reasoning usage",
|
||||
},
|
||||
@@ -983,7 +983,7 @@ test("chat pipeline translates OpenAI requests to Claude and returns OpenAI-shap
|
||||
const response = await handleChat(
|
||||
buildRequest({
|
||||
body: {
|
||||
model: "claude/claude-3-5-sonnet-20241022",
|
||||
model: "claude/claude-sonnet-4-6",
|
||||
stream: false,
|
||||
messages: [{ role: "user", content: "Hello Claude" }],
|
||||
},
|
||||
@@ -1566,7 +1566,7 @@ test("chat pipeline falls back across combo models when the first provider fails
|
||||
name: "combo-fallback",
|
||||
strategy: "priority",
|
||||
config: { maxRetries: 0, retryDelayMs: 0 },
|
||||
models: ["openai/gpt-4o-mini", "claude/claude-3-5-sonnet-20241022"],
|
||||
models: ["openai/gpt-4o-mini", "claude/claude-sonnet-4-6"],
|
||||
});
|
||||
const attempts = [];
|
||||
|
||||
|
||||
Reference in New Issue
Block a user