Chat chrome docks into the rail; drop the notes-off banner
The chat header and composer are full-bleed bands inside the main pane, which starts right at the sidebar on desktop - so both ended in a hard vertical edge beside the floating rail card (the 12px gutter showed the raw scene and the bands' fill + backdrop blur read as a color step at the seam), with their hairlines starting in mid-air at that edge. Both bands now reach the card's right edge (the rail's own 12px padding, expanded or collapsed) and their hairlines are inset to the content box; phones keep the plain full-width borders. The chat suite pins the dock. The "Chat is set to ignore notes text" banner went away - the scope chip already says what the chat may read. Version 0.5.5, SW -> freezmo-v77. Chat 104/104 (2 new), mobile 126/126, wizard 95/95, phase3 31/31, bubbles 34/34, i18n 13/13, native 33/33, regression 44/44, contrast 0 findings, axe clean. Full e2e: 14/14 suites.
This commit is contained in:
parent
7da308fa1e
commit
a5d30a171c
8 changed files with 124 additions and 20 deletions
17
README.md
17
README.md
|
|
@ -1419,9 +1419,20 @@ Things I chose while building, with the reasoning:
|
|||
chips, input, chips, note — shares one left edge.
|
||||
|
||||
In Aero the welcome panel is the same frosted glass as the cards (blurred, not a flat tint) and
|
||||
stretches flush with the header and the composer (measured: 268..1280 at a 1280 window, exactly
|
||||
the chrome's edges) — a flat pane floating 40px inside the scene read as "cut off next to the
|
||||
sidebar".
|
||||
stretches to the content column's edges — flush under the header and above the composer
|
||||
(measured: 268..1280 at a 1280 window) — a flat pane floating 40px inside the scene read as
|
||||
"cut off next to the sidebar".
|
||||
|
||||
87. **Chat's chrome docks into the rail (the sidebar seam).** The chat header and composer are
|
||||
full-bleed bands inside the main pane; at desktop widths that pane starts right at the sidebar,
|
||||
so both bands ended in a hard vertical edge beside the floating rail card — the 12px gutter
|
||||
showed the raw scene, and the bands' fill + backdrop blur made it read as a color step at the
|
||||
seam, with their hairlines starting in mid-air at that edge. Both bands now reach the card's
|
||||
right edge (the rail's own 12px padding, expanded or collapsed) and their hairlines are inset
|
||||
to the content box instead of touching the seam; phones keep the plain full-width borders. The
|
||||
chat suite pins the dock (band edge === card edge, zeroed band borders, inset `::after`
|
||||
hairlines). The "Chat is set to ignore notes text" banner went away with it — the scope chip
|
||||
already says what the chat may read.
|
||||
|
||||
86. **Reduced motion no longer ghosts the sheets.** The in-app toggle fed framer through
|
||||
`MotionConfig reducedMotion="always"`, which only strips *transform* animations — while every
|
||||
|
|
|
|||
|
|
@ -690,11 +690,15 @@
|
|||
}
|
||||
|
||||
/* Chat header: the same glass as the cards, a touch more fill — its title and
|
||||
scope line sit directly over the sky. */
|
||||
scope line sit directly over the sky. At lg its divider is the inset hairline
|
||||
the desktop docking block below draws (::after), so it never hits the seam. */
|
||||
.aero .chat-header {
|
||||
background-color: rgb(232 248 255 / 0.4);
|
||||
border-bottom: 1px solid rgb(255 255 255 / 0.6);
|
||||
}
|
||||
.aero .chat-header::after {
|
||||
background: rgb(255 255 255 / 0.6);
|
||||
}
|
||||
|
||||
/* The chat composer: one flat frosted panel for the input, the chips and the
|
||||
note — instead of each of them floating on the wallpaper. */
|
||||
|
|
@ -703,6 +707,56 @@
|
|||
border-top: 1px solid rgb(255 255 255 / 0.65);
|
||||
box-shadow: 0 -16px 36px -26px rgb(2 62 110 / 0.5);
|
||||
}
|
||||
.aero .chat-composer::after {
|
||||
background: rgb(255 255 255 / 0.65);
|
||||
}
|
||||
|
||||
/* Desktop: the chat chrome docks into the rail. The header and the composer
|
||||
are full-bleed bands inside the main pane; at lg that pane starts exactly
|
||||
at the sidebar, so both bands used to stop in a hard vertical edge beside
|
||||
the floating rail card — the scene showed through the 12px gutter and the
|
||||
bands' own fill/blur made it read as a color step at the seam. They now
|
||||
reach the card's right edge (the rail's own 12px padding, in the expanded
|
||||
and the collapsed state alike) and their hairlines span the content box
|
||||
instead of starting in mid-air at the seam. */
|
||||
@media (min-width: 1024px) {
|
||||
.chat-header {
|
||||
margin-left: -0.75rem;
|
||||
padding-left: 1.75rem;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.aero .chat-header {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.chat-header::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 1.75rem;
|
||||
right: 1rem;
|
||||
bottom: 0;
|
||||
height: 1px;
|
||||
background: var(--glass-border);
|
||||
}
|
||||
.chat-composer {
|
||||
margin-left: -0.75rem;
|
||||
}
|
||||
.chat-composer-band {
|
||||
padding-left: 3.25rem;
|
||||
border-top: 0;
|
||||
}
|
||||
.aero .chat-composer {
|
||||
border-top: 0;
|
||||
}
|
||||
.chat-composer::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 3.25rem;
|
||||
right: 2.5rem;
|
||||
top: 0;
|
||||
height: 1px;
|
||||
background: var(--glass-border);
|
||||
}
|
||||
}
|
||||
|
||||
/* The chat welcome (subtitle + suggestions): the same frosted glass as the
|
||||
cards (the shared recipe above), stretched flush with the header and the
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useAppReducedMotion as useReducedMotion } from "@/lib/motion-pref";
|
||||
import Link from "next/link";
|
||||
|
||||
import { Composer, type PendingAttachment } from "./Composer";
|
||||
import { ContextPicker } from "./ContextPicker";
|
||||
|
|
@ -769,14 +768,6 @@ export function ChatScreen() {
|
|||
/>
|
||||
</div>
|
||||
|
||||
{settings.chatNotes === false && scope.kind !== "none" ? (
|
||||
<p className="border-t border-[var(--hairline-soft)] bg-[var(--surface-sunken)] px-4 py-2 text-[11.5px] text-[var(--text-subtle)]">
|
||||
{t("chat.notesOffNote")}{" "}
|
||||
<Link href="/" className="underline decoration-dotted">
|
||||
{t("chat.notesOffLink")}
|
||||
</Link>
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<HistorySheet
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ export function Composer({
|
|||
const canSend = online && !busy && value.trim().length > 0;
|
||||
|
||||
return (
|
||||
<div className="border-t border-[var(--glass-border)] bg-[var(--glass-bg)] px-4 pb-safe pt-2 backdrop-blur-xl lg:px-10">
|
||||
<div className="chat-composer-band border-t border-[var(--glass-border)] bg-[var(--glass-bg)] px-4 pb-safe pt-2 backdrop-blur-xl lg:px-10">
|
||||
{attachments.length > 0 ? (
|
||||
<ul className="mb-2 flex flex-wrap gap-1.5">
|
||||
{attachments.map((entry, index) => (
|
||||
|
|
|
|||
|
|
@ -945,8 +945,6 @@ const en: Record<string, string> = {
|
|||
"chat.emptyHint": "Answers come from your own decks. Pick what the chat may read with the context chip below.",
|
||||
"chat.oldDecksNote": "Older decks only have the generated study content. Re-add the notes for better answers.",
|
||||
"chat.fromLabel": "From",
|
||||
"chat.notesOffNote": "Chat is set to ignore notes text.",
|
||||
"chat.notesOffLink": "Turn it back on in Settings",
|
||||
"chat.attach": "Attach photos or documents",
|
||||
"chat.attachPhoto": "photo",
|
||||
"chat.attachDoc": "document",
|
||||
|
|
@ -1931,8 +1929,6 @@ const cs: Record<string, string> = {
|
|||
"chat.emptyHint": "Odpovědi vycházejí z tvých sad. Pomocí chipu s kontextem níže vyber, co smí chat číst.",
|
||||
"chat.oldDecksNote": "Starší sady mají jen vygenerovaný obsah. Přidej znovu poznámky a odpovědi budou lepší.",
|
||||
"chat.fromLabel": "Z",
|
||||
"chat.notesOffNote": "Chat má vypnuté používání textu poznámek.",
|
||||
"chat.notesOffLink": "Zapnout v nastavení",
|
||||
"chat.attach": "Přiložit fotky nebo dokumenty",
|
||||
"chat.attachPhoto": "fotka",
|
||||
"chat.attachDoc": "dokument",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "freezmo",
|
||||
"version": "0.5.4",
|
||||
"version": "0.5.5",
|
||||
"private": true,
|
||||
"license": "GPL-3.0-only",
|
||||
"scripts": {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
*
|
||||
* Bumping CACHE_VERSION is how a new deploy invalidates old assets.
|
||||
*/
|
||||
const CACHE_VERSION = "freezmo-v76";
|
||||
const CACHE_VERSION = "freezmo-v77";
|
||||
const SHELL_CACHE = `${CACHE_VERSION}-shell`;
|
||||
const ASSET_CACHE = `${CACHE_VERSION}-assets`;
|
||||
|
||||
|
|
|
|||
|
|
@ -291,6 +291,58 @@ try {
|
|||
await desktop.page.waitForTimeout(400);
|
||||
check("desktop: AI disclaimer under the composer", await desktop.page.getByText("Freezmo uses AI and can make mistakes").isVisible());
|
||||
|
||||
/* The chat chrome docks into the rail: the header and the composer used to
|
||||
end in a hard vertical edge beside the floating rail card (the scene
|
||||
showed through the gutter and their fill/blur made it read as a color
|
||||
step at the seam), and their hairlines started right at that edge. Both
|
||||
bands now reach the card's right edge and their hairlines are inset to
|
||||
the content box. */
|
||||
const seam = await desktop.page.evaluate(() => {
|
||||
const card = document.querySelector(".sidebar-surface");
|
||||
const header = document.querySelector(".chat-header");
|
||||
const band = document.querySelector(".chat-composer-band");
|
||||
if (!card || !header || !band) return null;
|
||||
const line = (el) => {
|
||||
const cs = getComputedStyle(el, "::after");
|
||||
return { content: cs.content, left: parseFloat(cs.left), right: parseFloat(cs.right) };
|
||||
};
|
||||
return {
|
||||
cardRight: Math.round(card.getBoundingClientRect().right),
|
||||
headerLeft: Math.round(header.getBoundingClientRect().left),
|
||||
bandLeft: Math.round(band.getBoundingClientRect().left),
|
||||
headerBorder: getComputedStyle(header).borderBottomWidth,
|
||||
bandBorder: getComputedStyle(band).borderTopWidth,
|
||||
headerLine: line(header),
|
||||
bandLine: line(band.parentElement),
|
||||
};
|
||||
});
|
||||
check(
|
||||
"desktop: chat chrome docks into the rail (no seam)",
|
||||
seam !== null &&
|
||||
seam.headerLeft === seam.cardRight &&
|
||||
seam.bandLeft === seam.cardRight &&
|
||||
seam.headerBorder === "0px" &&
|
||||
seam.bandBorder === "0px" &&
|
||||
seam.headerLine.content !== "none" &&
|
||||
seam.headerLine.left > 0 &&
|
||||
seam.bandLine.content !== "none" &&
|
||||
seam.bandLine.left > 0,
|
||||
JSON.stringify(seam),
|
||||
);
|
||||
|
||||
/* The notes-off banner is gone: with "keep notes text" off, chat no longer
|
||||
nags at the bottom — the scope chip already says what it reads. */
|
||||
const quiet = await makePage(browser, { width: 1280, height: 900, deviceScaleFactor: 1 });
|
||||
await seed(quiet.page, {
|
||||
decks: [DECK_NOTES],
|
||||
extra: { "freezmo.settings.v1": JSON.stringify({ apiKey: "test-key", chatNotes: false }) },
|
||||
});
|
||||
await quiet.page.goto(`${APP}/chat`, { waitUntil: "networkidle" });
|
||||
await quiet.page.waitForTimeout(900);
|
||||
const quietText = await quiet.page.evaluate(() => document.body.innerText);
|
||||
check("desktop: no notes-off banner with chat notes off", !/ignore notes text|ignoruje/i.test(quietText));
|
||||
await quiet.context.close();
|
||||
|
||||
/* 6b. desktop pickers: the chip that opens also closes ---------------
|
||||
(It used to only ever open, so the only way out was a choice.) */
|
||||
const modelChip = () => desktop.page.getByRole("button", { name: /Stub (Text|Vision)|Chat model/ }).first();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue