Compare commits

...

3 Commits

Author SHA1 Message Date
diegosouzapw
45424ca226 fix(ci): docs-sync, openapi version, changelog format, pre-commit hook
- docs/openapi.yaml: update info.version from 2.3.6 to 2.4.1 (fixes CI check)
- CHANGELOG.md: add '## [Unreleased]' section as first heading (required by check-docs-sync)
- scripts/check-docs-sync.mjs: fix regex to accept both hyphen (-) and em-dash (—)
  as date separators in changelog headings (standard Keep a Changelog format)
- .husky/pre-commit: add 'node scripts/check-docs-sync.mjs' to catch version
  mismatches locally before push
2026-03-13 11:45:32 -03:00
diegosouzapw
d976abb5e0 chore: v2.4.1 — combos free-stack always visible 2026-03-13 11:29:51 -03:00
diegosouzapw
92d302aed3 fix(combos): free-stack template first, 2x2 grid, green highlight badge
- Move 'Free Stack ($0)' to position 1 in COMBO_TEMPLATES (was 4th, invisible in 3-col grid)
- Add isFeatured flag to free-stack for special styling
- Change template grid: grid-cols-3 → 2x2 (sm:grid-cols-2) — all 4 templates visible
- Free Stack: green border/bg (emerald), FREE badge, larger text size
- Other templates: hover styles preserved, → arrow on Apply link
- Increase templates section padding
2026-03-13 11:26:18 -03:00
7 changed files with 48 additions and 29 deletions

View File

@@ -1 +1,2 @@
npx lint-staged
node scripts/check-docs-sync.mjs

View File

@@ -1,5 +1,13 @@
# Changelog
## [Unreleased]
## [2.4.1] - 2026-03-13
### 🐛 Fix
- **Combos modal: Free Stack visible and prominent** — Free Stack template was hidden (4th in 3-column grid). Fixed: moved to position 1, switched to 2x2 grid so all 4 templates are visible, green border + FREE badge highlight.
## [2.4.0] - 2026-03-13
> **Major release** — Free Stack ecosystem, transcription playground overhaul, 44+ providers, comprehensive free tier documentation, and UI improvements across the board.

View File

@@ -1,7 +1,7 @@
openapi: 3.1.0
info:
title: OmniRoute API
version: 2.3.6
version: 2.4.1
description: |
OmniRoute is a local-first AI API proxy router. It provides an OpenAI-compatible
endpoint that routes requests to multiple AI providers with load balancing,

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "omniroute",
"version": "2.3.17",
"version": "2.4.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "omniroute",
"version": "2.3.17",
"version": "2.4.1",
"hasInstallScript": true,
"license": "MIT",
"workspaces": [

View File

@@ -1,6 +1,6 @@
{
"name": "omniroute",
"version": "2.4.0",
"version": "2.4.1",
"description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.",
"type": "module",
"bin": {

View File

@@ -43,7 +43,7 @@ function extractOpenApiVersion(content) {
}
function extractChangelogSections(content) {
const headings = [...content.matchAll(/^##\s+\[([^\]]+)\](?:\s+—\s+.*)?$/gm)];
const headings = [...content.matchAll(/^##\s+\[([^\]]+)\](?:\s+[-—–].*)?$/gm)];
return headings.map((match) => match[1]);
}

View File

@@ -145,6 +145,22 @@ const COMBO_TEMPLATE_FALLBACK = {
};
const COMBO_TEMPLATES = [
{
id: "free-stack",
icon: "volunteer_activism",
titleKey: "templateFreeStack",
descKey: "templateFreeStackDesc",
fallbackTitle: COMBO_TEMPLATE_FALLBACK.freeStackTitle,
fallbackDesc: COMBO_TEMPLATE_FALLBACK.freeStackDesc,
strategy: "round-robin",
suggestedName: "free-stack",
isFeatured: true,
config: {
maxRetries: 3,
retryDelayMs: 500,
healthCheckEnabled: true,
},
},
{
id: "high-availability",
icon: "shield",
@@ -190,21 +206,6 @@ const COMBO_TEMPLATES = [
healthCheckEnabled: true,
},
},
{
id: "free-stack",
icon: "volunteer_activism",
titleKey: "templateFreeStack",
descKey: "templateFreeStackDesc",
fallbackTitle: COMBO_TEMPLATE_FALLBACK.freeStackTitle,
fallbackDesc: COMBO_TEMPLATE_FALLBACK.freeStackDesc,
strategy: "round-robin",
suggestedName: "free-stack",
config: {
maxRetries: 3,
retryDelayMs: 500,
healthCheckEnabled: true,
},
},
];
function getStrategyMeta(strategy) {
@@ -1486,7 +1487,7 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
</div>
{!isEdit && (
<div className="rounded-lg border border-black/10 dark:border-white/10 bg-black/[0.02] dark:bg-white/[0.02] p-2.5">
<div className="rounded-lg border border-black/8 dark:border-white/8 bg-black/[0.02] dark:bg-white/[0.02] p-3">
<div className="mb-2">
<p className="text-xs font-medium">
{getI18nOrFallback(t, "templatesTitle", COMBO_TEMPLATE_FALLBACK.title)}
@@ -1499,27 +1500,36 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders }) {
)}
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-1.5">
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2 mt-1">
{COMBO_TEMPLATES.map((template) => (
<button
type="button"
key={template.id}
onClick={() => applyTemplate(template)}
className="text-left rounded-md border border-black/10 dark:border-white/10 bg-white/70 dark:bg-white/[0.03] px-2 py-1.5 hover:border-primary/40 hover:bg-primary/5 transition-colors"
className={`text-left rounded-md border px-3 py-2 transition-all ${
template.isFeatured
? "border-emerald-500/50 bg-emerald-500/5 hover:border-emerald-500/80 hover:bg-emerald-500/10 ring-1 ring-emerald-500/20"
: "border-black/10 dark:border-white/10 bg-white/70 dark:bg-white/[0.03] hover:border-primary/40 hover:bg-primary/5"
}`}
>
<div className="flex items-center gap-1.5">
<span className="material-symbols-outlined text-[14px] text-primary">
<div className="flex items-center gap-2">
<span className={`material-symbols-outlined text-[16px] ${template.isFeatured ? "text-emerald-500" : "text-primary"}`}>
{template.icon}
</span>
<span className="text-[11px] font-medium text-text-main">
<span className="text-[12px] font-semibold text-text-main">
{getI18nOrFallback(t, template.titleKey, template.fallbackTitle)}
</span>
{template.isFeatured && (
<span className="ml-auto text-[9px] font-bold uppercase tracking-wide bg-emerald-500/20 text-emerald-600 dark:text-emerald-400 px-1.5 py-0.5 rounded">
FREE
</span>
)}
</div>
<p className="text-[10px] text-text-muted mt-1 leading-4">
<p className="text-[10px] text-text-muted mt-1.5 leading-[1.5]">
{getI18nOrFallback(t, template.descKey, template.fallbackDesc)}
</p>
<p className="text-[10px] text-primary mt-1">
{getI18nOrFallback(t, "templateApply", COMBO_TEMPLATE_FALLBACK.apply)}
<p className={`text-[10px] mt-1.5 font-medium ${template.isFeatured ? "text-emerald-500" : "text-primary"}`}>
{getI18nOrFallback(t, "templateApply", COMBO_TEMPLATE_FALLBACK.apply)}
</p>
</button>
))}