Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ This file is the release-notes source of truth for Marinara Engine. Reuse these

- Added a Marinara-specific AI agent workflow overlay, adapted from the Chai Agent Workflow Pack, covering proof discipline, bugfix/feature lanes, issue filing, PR gates, and risky-work claim boundaries.
- Added a None option for Roleplay message avatars so messages can render without avatar attachments.
- Added default starting values for numeric Game HUD widgets, with setup/editor clamps that keep start values within the configured max.
- Added a prompt override editor for registered prompt templates, including conversation selfie overrides, collapsible settings, and draft preservation.
- Added shared drag-and-drop image upload dropzones for character avatars, chat gallery images, and background imports.
- Added a manual Game Mode combat start control with confirmation so players can trigger encounter setup when a scene should enter combat.
- Added a General quote-format preference for straight or curly dialogue quotes and apostrophes, with editor/input formatting support across chat, presets, characters, and personas.
- Added conditional prompt macros, macro comment blocks, and Macro Reference guidance so presets and character/persona cards can keep author-only notes or branch prompt text by speaker/character.
Expand All @@ -29,6 +32,8 @@ This file is the release-notes source of truth for Marinara Engine. Reuse these

- Removed the Conversation, Roleplay, and Game mode shortcuts from the topbar because the sidebar already owns mode navigation.
- Widened the Glued Side Panel roleplay avatar presentation so the portrait strip has more visual presence.
- Improved sprite wand cleanup with halo edge cleanup, clean/paint brush tools, unified brush controls, and better multi-pointer handling.
- Polished Tracker Panel visual controls, thought bubbles, persona/tracker card styling, responsive world-state temperature display, and color preview restore/legacy tint behavior.
- Improved Game Mode combat setup so encounter generation can run in the background after scene analysis, with debug logging and a wait state only when the player reaches combat before setup is ready.
- Removed unreliable met/unmet status tracking from Game Mode NPC prompt context.
- Improved Roleplay group chat Individual mode prompting so only the currently responding character card is included, other characters' prior messages are treated as user-side context, and the turn-owner instruction can be toggled.
Expand All @@ -44,9 +49,15 @@ This file is the release-notes source of truth for Marinara Engine. Reuse these

### Fixed

- Fixed mobile Conversation chats where optional toolbar actions could squeeze the message textarea down until it appeared missing on narrow phone viewports.
- Fixed tracker character-card lookup so active-chat card aliases from title/comment text resolve tracker rows before out-of-chat fallback cards, keeping group-chat tracker portraits and color settings attached to the intended character.
- Fixed character tracker refreshes preserving user-uploaded NPC portraits and portrait framing across agent updates, including first snapshot writes.
- Fixed the Roleplay HUD temperature chip so it respects the shared Tracker Panel Celsius/Fahrenheit display setting.
- Added a stale client artifact cleanup step for the obsolete tracker data sidebar folder so installs, updates, checks, and builds are not tripped up by leftover local files after the tracker panel refactor.
- Fixed Docker builds so the stale client artifact cleanup script is available before dependency install/build scripts run.
- Fixed streaming Roleplay messages in Glued Side Panel avatar mode so the avatar frame keeps the selected scale and is revealed by the growing message instead of rescaling while tokens arrive.
- Fixed Quest Board state merges so tracker updates no longer revert quest progress or keep completed empty-objective quests.
- Fixed profile export/import fallback handling for large assets so profile exports can recover cleanly when embedded asset payloads are too large.
- Fixed Windows installer updates for existing shallow release checkouts by fetching the resolved release commit before checkout.
- Fixed mobile Game Mode character and party controls so sheet actions stay compact, long character names can remain accessible, and crowded party rosters collapse into a scrollable mobile party picker.
- Fixed mobile Game Mode choice prompts so large choice sets stay readable and scroll inside the available play area instead of squishing buttons or pushing custom input off-screen.
Expand Down Expand Up @@ -81,6 +92,8 @@ This file is the release-notes source of truth for Marinara Engine. Reuse these
- Added explicit Illustrator try-again controls when image generation fails, including a toast action and a persistent Roleplay HUD retry button. ([#797](https://github.com/Pasta-Devs/Marinara-Engine/issues/797))
- Added Local Model sidecar as a first-class embedding source, including an Embedding Connection option, lorebook vectorization support, and a stable `/api/sidecar/v1/embeddings` endpoint. ([#780](https://github.com/Pasta-Devs/Marinara-Engine/issues/780))
- Added opt-in Turn Data Access settings for custom post-processing agents so they can receive current-turn pre-generation injections and parallel agent results without exposing that data to existing agents by default. ([#778](https://github.com/Pasta-Devs/Marinara-Engine/issues/778))
- Added Memory Recall export/import for moving chat recall data between profiles or installs.
- Added weighted random macro choices for SillyTavern-style random prompt variants.
- Added a native Appearance background blur slider for Roleplay and Game mode backgrounds. ([#763](https://github.com/Pasta-Devs/Marinara-Engine/issues/763))
- Added excluded-tag filtering for the character browser, including `-tag:"tag name"` search syntax and exclude toggles in the character tag picker. ([#702](https://github.com/Pasta-Devs/Marinara-Engine/issues/702))
- Added a server-side autonomous conversation scheduler so enabled characters can generate restrained scheduled messages while the browser poller is absent, with client-presence checks to avoid duplicate client/server generations. ([#698](https://github.com/Pasta-Devs/Marinara-Engine/issues/698))
Expand Down Expand Up @@ -124,6 +137,8 @@ This file is the release-notes source of truth for Marinara Engine. Reuse these
- Fixed Game Mode asset generation prompt review, NPC portrait matching, sprite recovery, Professor-name avatar matching, and command-prompt regeneration replay.
- Fixed Game Session Log flicker, deletion offsets, manual deletion persistence, and dice-roll dismissal when advancing dialogue.
- Fixed Game Mode weather, storm ambience, sun overlay behavior, CYOA live updates, skill checks, inventory notifications, combat voice audio, mobile party access, tracker refreshes, and tracker edit persistence.
- Fixed CJK Google font shard loading and scene-summary max-token overrides.
- Fixed manual chat file deletion persistence and regenerate replay for command prompts.
- Fixed Conversation disconnection aborts on Docker, markdown block preservation, hidden-message regeneration crashes, Up Arrow recall behavior, role editing, DM schedule inheritance, random connection schedule generation, and connected-chat placeholder branch names.
- Fixed character avatar uploads preserving unsaved drafts, chat folder click targets, drag reorder behavior, text selection while dragging, folder storage atomicity, and Professor Mari continuation after tool/fetch work.
- Fixed OpenAI ChatGPT request shape and SSE parsing, compressed provider JSON decoding (`gzip`, raw `gzip`, and Brotli), Gemini gzip decoding, provider identity handling, NovelAI V4 prompt/model handling, ComfyUI numeric workflow placeholders, Horde image endpoints, and Pygmalion avatar content-type fallback.
Expand Down Expand Up @@ -154,6 +169,7 @@ This file is the release-notes source of truth for Marinara Engine. Reuse these
- Per-connection max parallel agent job controls, allowing agent-heavy chats to split same-connection work across multiple LLM calls.
- Editable Game Session History map JSON in the current-session spoiler section.
- Markdown rendering and live preview for Game journal notes.
- Tracker Data Sidebar for viewing and editing live tracker data from the side panel.
- `/emote name="Character" expression="expression"` for listing and manually switching roleplay sprite expressions.
- Duplicate action for individual prompt preset blocks.
- Close controls for Game mode choice prompts and quick-time event windows.
Expand Down
10 changes: 5 additions & 5 deletions packages/client/src/components/chat/ConversationInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1569,7 +1569,7 @@ export function ConversationInput({
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={cn(
"relative flex items-center gap-1 rounded-2xl border-2 bg-[var(--card)] px-2 py-1.5 transition-all duration-200 sm:gap-2 sm:px-4 sm:py-2.5 dark:bg-black/40",
"relative flex flex-wrap items-end gap-1 rounded-2xl border-2 bg-[var(--card)] px-2 py-1.5 transition-all duration-200 sm:flex-nowrap sm:items-center sm:gap-2 sm:px-4 sm:py-2.5 dark:bg-black/40",
isDragging ? "border-blue-400/50 bg-blue-500/10 shadow-lg shadow-blue-500/10" : "border-[var(--border)]",
)}
>
Expand All @@ -1588,7 +1588,7 @@ export function ConversationInput({
<button
onClick={() => fileInputRef.current?.click()}
className={cn(
"flex h-11 w-11 items-center justify-center rounded-xl transition-all active:scale-90 sm:h-8 sm:w-8",
"order-2 flex h-11 w-11 items-center justify-center rounded-xl transition-all active:scale-90 sm:order-none sm:h-8 sm:w-8",
attachments.length
? "bg-foreground/10 text-foreground/75"
: "text-foreground/40 hover:bg-foreground/10 hover:text-foreground/70",
Expand All @@ -1601,7 +1601,7 @@ export function ConversationInput({
{/* Quick Switchers — desktop: inline, mobile: chevron */}
<QuickConnectionSwitcher className="hidden sm:flex" />
<QuickPersonaSwitcher className="hidden sm:flex" />
<div className="sm:hidden">
<div className="order-2 sm:hidden">
<QuickSwitcherMobile />
</div>

Expand All @@ -1624,11 +1624,11 @@ export function ConversationInput({
onInput={handleInput}
onKeyDown={handleKeyDown}
onPaste={handlePaste}
className="max-h-[12.5rem] min-w-0 flex-1 resize-none bg-transparent py-0 text-[1rem] leading-normal text-[var(--foreground)] outline-none placeholder:text-foreground/30"
className="order-1 max-h-[12.5rem] min-w-0 basis-full flex-1 resize-none bg-transparent py-0 text-[1rem] leading-normal text-[var(--foreground)] outline-none placeholder:text-foreground/30 sm:order-none sm:basis-auto"
/>

{/* Right actions */}
<div className="flex shrink-0 items-center gap-0.5">
<div className="order-2 ml-auto flex min-w-0 flex-1 flex-nowrap items-center justify-end gap-0.5 sm:order-none sm:flex-none sm:shrink-0">
<div className="relative">
<button
ref={gifButtonRef}
Expand Down
56 changes: 19 additions & 37 deletions packages/client/src/components/chat/RoleplayHUD.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ import { useAgentConfigs, useCustomAgentRuns, type AgentConfigRow } from "../../
import { useChat } from "../../hooks/use-chats";
import { discardPendingGameStatePatch, useGameStatePatcher } from "../../hooks/use-game-state-patcher";
import { useUIStore } from "../../stores/ui.store";
import {
getTemperatureColor,
getTemperatureGaugeDisplay,
getTemperatureKeywordHint,
parseTemperatureValue,
} from "../../features/tracker-panel/lib/world-state-display";
import type {
GameState,
PresentCharacter,
Expand All @@ -38,7 +44,7 @@ import type {
CustomTrackerField,
Message,
} from "@marinara-engine/shared";
import type { HudPosition } from "../../stores/ui.store";
import type { HudPosition, TrackerTemperatureUnit } from "../../stores/ui.store";

const ACTIONS_DROPDOWN_WIDTH_PX = 288;

Expand Down Expand Up @@ -142,6 +148,7 @@ export function RoleplayHUD({
const trackerPanelEnabled = useUIStore((s) => s.trackerPanelEnabled);
const trackerPanelOpen = useUIStore((s) => s.trackerPanelOpen);
const trackerPanelHideHudWidgets = useUIStore((s) => s.trackerPanelHideHudWidgets);
const trackerTemperatureUnit = useUIStore((s) => s.trackerTemperatureUnit);
const toggleTrackerPanel = useUIStore((s) => s.toggleTrackerPanel);

const isTrackerBusy = isAgentProcessing || isStreaming || gameStateRefreshing;
Expand Down Expand Up @@ -269,6 +276,7 @@ export function RoleplayHUD({
time={time ?? ""}
weather={weather ?? ""}
temperature={temperature ?? ""}
trackerTemperatureUnit={trackerTemperatureUnit}
onSaveLocation={(v) => patchField("location", v)}
onSaveDate={(v) => patchField("date", v)}
onSaveTime={(v) => patchField("time", v)}
Expand Down Expand Up @@ -334,6 +342,7 @@ export function RoleplayHUD({
time={time ?? ""}
weather={weather ?? ""}
temperature={temperature ?? ""}
trackerTemperatureUnit={trackerTemperatureUnit}
onSaveLocation={(v) => patchField("location", v)}
onSaveDate={(v) => patchField("date", v)}
onSaveTime={(v) => patchField("time", v)}
Expand Down Expand Up @@ -1264,6 +1273,7 @@ function CombinedWorldWidget({
time,
weather,
temperature,
trackerTemperatureUnit,
onSaveLocation,
onSaveDate,
onSaveTime,
Expand All @@ -1278,6 +1288,7 @@ function CombinedWorldWidget({
time: string;
weather: string;
temperature: string;
trackerTemperatureUnit: TrackerTemperatureUnit;
onSaveLocation: (v: string) => void;
onSaveDate: (v: string) => void;
onSaveTime: (v: string) => void;
Expand All @@ -1291,18 +1302,10 @@ function CombinedWorldWidget({
const buttonRef = useRef<HTMLButtonElement>(null);
const weatherEmoji = weather ? getWeatherEmoji(weather) : "🌤️";
const pinColor = getLocationPinColor(location);
const tempNumeric = temperature ? parseTemperature(temperature) : null;
const temperatureDisplay = getTemperatureGaugeDisplay(temperature, trackerTemperatureUnit);
const tempNumeric = temperature ? parseTemperatureValue(temperature) : null;
const temp = tempNumeric ?? (temperature ? getTemperatureKeywordHint(temperature) : null);
const tempColor =
temp !== null
? temp < 0
? "text-blue-400"
: temp < 15
? "text-sky-400"
: temp < 30
? "text-amber-400"
: "text-red-400"
: "text-rose-400/50";
const tempColor = getTemperatureColor(temperature);

// Dynamic calendar: show day number
const dateParts = date ? parseDateLabel(date) : { day: null, month: null };
Expand All @@ -1313,10 +1316,9 @@ function CombinedWorldWidget({
const hourAngle = hour >= 0 ? (hour % 12) * 30 + minute * 0.5 : 0;
const minuteAngle = minute * 6;

// Thermometer fill fraction (clamp -20..50°C → 0..1)
const tempFill = temp !== null ? Math.max(0, Math.min(1, (temp + 20) / 70)) : 0.3;
const tempFillColor =
temp !== null ? (temp < 0 ? "#60a5fa" : temp < 15 ? "#38bdf8" : temp < 30 ? "#fbbf24" : "#f87171") : "#fb7185";
const tempFill =
temperatureDisplay.percent == null ? 0.3 : Math.max(0, Math.min(1, temperatureDisplay.percent / 100));
const tempFillColor = temperatureDisplay.color;

return (
<div className="relative">
Expand Down Expand Up @@ -1462,7 +1464,7 @@ function CombinedWorldWidget({
</svg>
{tempNumeric !== null && (
<span className={cn("text-[0.5rem] md:text-[0.5625rem] font-bold leading-none shrink-0", tempColor)}>
{tempNumeric}°
{temperatureDisplay.label}
</span>
)}
</button>
Expand Down Expand Up @@ -1567,26 +1569,6 @@ function getWeatherEmoji(weather: string): string {
return "🌤️";
}

function parseTemperature(temp: string): number | null {
const m = temp.match(/-?\d+(\.\d+)?/);
if (!m) return null;
const num = parseFloat(m[0]!);
if (/°?\s*f/i.test(temp)) return Math.round((num - 32) * (5 / 9));
return Math.round(num);
}

/** Map descriptive temperature words to a numeric-equivalent hint (°C). */
function getTemperatureKeywordHint(text: string): number | null {
const t = text.toLowerCase();
if (/\b(freez|frigid|arctic|glacial|sub-?zero|blizzard)/.test(t)) return -10;
if (/\b(cold|chill|frost|wintry|icy|bitter|nipp)/.test(t)) return 2;
if (/\b(cool|brisk|crisp|refresh)/.test(t)) return 12;
if (/\b(mild|pleasant|comfort|temperate|fair)/.test(t)) return 20;
if (/\b(warm|balmy|toasty|muggy|humid|stuffy|sultry)/.test(t)) return 28;
if (/\b(hot|swelter|blaz|scorch|burn|heat|boil|sear|bak)/.test(t)) return 38;
return null;
}

/** Categorise location text into a colour for the map-pin icon. */
function getLocationPinColor(location: string): string {
const l = location.toLowerCase();
Expand Down
Loading
Loading