mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-13 11:57:19 +01:00
fix(render): offer the Aside remedy only on macOS (#3735); fix docs/skills.md vision-model line (#3375) (#4131)
* fix(render): offer the Aside remedy only on macOS (#3735) NO_BROWSER_HELP told Linux and Windows hosts to open the Aside app, which ships only for macOS. noBrowserHelp(platform) now adds the Aside remedy on darwin only and keeps the ./setup and GSTACK_BROWSE_BIN remedies everywhere. Also corrects docs/skills.md's /design-html refinement loop, which still said "Uses GPT-4o vision", to match the design-html template (#3375). Rewritten on main from outside PR #4122. Co-authored-by: szsunyuan <3579304+szsunyuan@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore(changelog): name the #3735 fragment after the branch Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: szsunyuan <3579304+szsunyuan@users.noreply.github.com> Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
6 files changed
+29
-6
No files matched your search
@@ -0,0 +1 @@
|
||||
- **Missing-browser errors stop telling Linux and Windows hosts to open the Aside app.** `NO_BROWSER_HELP`, used by `/make-pdf` and the renderer's no-browser error, now offers the Aside remedy only on macOS, where Aside ships, and keeps the `./setup` and `GSTACK_BROWSE_BIN` remedies on every platform. The docs/skills.md `/design-html` refinement loop also stops naming GPT-4o and describes the configured vision model, matching the template. (#3735, #3375; contributed by @szsunyuan in #4122)
|
||||
+1
-1
@@ -467,7 +467,7 @@ Not every page needs the full Pretext engine. The skill reads the design and pic
|
||||
### The refinement loop
|
||||
|
||||
1. Reads the approved mockup from `approved.json`, plus any page archetype and section outline you picked on the board, which it builds as given instead of asking again
|
||||
2. Uses GPT-4o vision to extract implementation spec (colors, typography, layout)
|
||||
2. Extracts an implementation spec (colors, typography, layout) with the provider's vision model (OpenAI gpt-5.5, or Gemini when only a Gemini key is set; `GSTACK_DESIGN_MODEL` overrides)
|
||||
3. Generates self-contained HTML with Pretext inlined (15KB, zero network dependency)
|
||||
4. Spins up a live-reload server so you see changes instantly
|
||||
5. Screenshots at 3 viewports (mobile, tablet, desktop) to verify layout
|
||||
|
||||
+6
-1
@@ -678,7 +678,12 @@ export async function renderWithBrowse(spec: RenderSpec, bin: string | null = re
|
||||
// ─── Engine choice ───────────────────────────────────────────────────────────
|
||||
|
||||
export const NO_BROWSER = 'no browser available';
|
||||
export const NO_BROWSER_HELP = "open the Aside app (macOS 15+, aside.com), or run ./setup in the gstack repo to build gstack's own headless browser (or point GSTACK_BROWSE_BIN at a browse binary)";
|
||||
/** Recovery advice for a missing browser. Aside ships only for macOS, so other platforms get only the bundled-browser remedies. */
|
||||
export function noBrowserHelp(platform: NodeJS.Platform = process.platform): string {
|
||||
const aside = platform === 'darwin' ? 'open the Aside app (macOS 15+, aside.com), or ' : '';
|
||||
return `${aside}run ./setup in the gstack repo to build gstack's own headless browser (or point GSTACK_BROWSE_BIN at a browse binary)`;
|
||||
}
|
||||
export const NO_BROWSER_HELP = noBrowserHelp();
|
||||
|
||||
export type EngineChoice =
|
||||
| { engine: 'aside'; version: string }
|
||||
|
||||
@@ -126,7 +126,7 @@ describe("renderPdf", () => {
|
||||
const none = async (): Promise<RenderResult> => ({ ok: false, outputs: [], evals: {}, stdout: "", error: `${NO_BROWSER}: ${NO_BROWSER_HELP} (NEEDS_ASIDE: aside not on PATH)` });
|
||||
const err = await renderPdf("<p></p>", { output: "/tmp/never.pdf" }, none).catch((e: Error) => e);
|
||||
expect(err).toBeInstanceOf(BrowserUnavailableError);
|
||||
expect((err as Error).message).toContain("aside.com");
|
||||
expect((err as Error).message).toContain(NO_BROWSER_HELP);
|
||||
expect((err as Error).message).toContain("./setup");
|
||||
expect((err as Error).message).toContain("GSTACK_BROWSE_BIN");
|
||||
});
|
||||
|
||||
@@ -124,7 +124,7 @@ const smokeFixture = () => path.join(OUTPUT_TMP_DIR, `make-pdf-smoke-${process.p
|
||||
// ─── [1/5] browser check ──────────────────────────────────────────────────────
|
||||
|
||||
describe("runSetup [1/5]: no browser at all", () => {
|
||||
test("Aside absent and no browse binary: FAIL, the NO_BROWSER text with both remedies, exit 4, never reaches [2/5]", async () => {
|
||||
test("Aside absent and no browse binary: FAIL, the NO_BROWSER text with this platform's remedies, exit 4, never reaches [2/5]", async () => {
|
||||
pickEngine(true, { probe: noAside, resolveBin: () => null });
|
||||
const run = await captureRun(() => runSetup());
|
||||
expect(run.error).toBeUndefined();
|
||||
@@ -156,7 +156,7 @@ describe.skipIf(isWin)("runSetup [2/5]: render smoke through gstack's own browse
|
||||
expect(run.stderr).toContain("[2/5] Rendering through gstack browser... FAIL");
|
||||
expect(run.stderr).toContain("gstack browser could not render a page: unexpected page text: not-the-smoke-page");
|
||||
expect(run.stderr).toContain(`To fix: ${NO_BROWSER_HELP}`);
|
||||
expect(run.stderr).toContain("open the Aside app");
|
||||
expect(run.stderr.includes("open the Aside app")).toBe(process.platform === "darwin");
|
||||
expect(run.stderr).toContain("./setup");
|
||||
expect(run.stderr).not.toContain("[3/5]");
|
||||
expect(smokeDirs(stage)).toEqual(before); // finally { rmSync(smokeDir) } ran despite the exit
|
||||
|
||||
@@ -14,7 +14,7 @@ import { spawnSync } from 'child_process';
|
||||
import {
|
||||
buildRenderScript, lengthToInches, paperInches, renderWithAside, RENDER_SENTINEL,
|
||||
resolveBrowseBin, browsePdfPayload, browseScreenshotArgs, renderWithBrowse, NO_BROWSER,
|
||||
serveDir, pickEngine, SAFE_TMP_DIR,
|
||||
serveDir, pickEngine, SAFE_TMP_DIR, noBrowserHelp, NO_BROWSER_HELP,
|
||||
type RenderSpec, type RenderResult, type AsideProbe, type EngineChoice,
|
||||
} from '../lib/aside-render';
|
||||
import { asideAvailable } from './helpers/aside-available';
|
||||
@@ -950,6 +950,23 @@ describe.skipIf(!HERMETIC)('aside-render: renderWithBrowse — daemon CLI contra
|
||||
}, 20_000);
|
||||
});
|
||||
|
||||
describe('aside-render: missing-browser advice (#3735)', () => {
|
||||
// Value: protects=Aside recovery advice only on macOS, bundled-browser advice everywhere; fails_when=Linux or Windows hosts are told to open the macOS-only Aside app, or a host loses the ./setup remedy; why_new=no test covered the advice per platform; seam=none
|
||||
test.each(['linux', 'win32', 'darwin'] as const)('%s gets only the remedies it can use', (platform) => {
|
||||
const help = noBrowserHelp(platform);
|
||||
expect(help).toContain('./setup');
|
||||
expect(help).toContain('GSTACK_BROWSE_BIN');
|
||||
expect(help.includes('open the Aside app')).toBe(platform === 'darwin');
|
||||
expect(help.includes('aside.com')).toBe(platform === 'darwin');
|
||||
});
|
||||
|
||||
test('NO_BROWSER_HELP and the no-engine error use this host\'s advice', () => {
|
||||
expect(NO_BROWSER_HELP).toBe(noBrowserHelp(process.platform));
|
||||
const choice = pickEngine(true, { probe: () => ({ ok: false, reason: 'NEEDS_ASIDE', detail: 'stub' }), resolveBin: () => null });
|
||||
expect(choice.engine === null && choice.error).toBe(`${NO_BROWSER}: ${NO_BROWSER_HELP} (NEEDS_ASIDE: stub)`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('aside-render: pickEngine — cached engine choice through the probe/resolver seam', () => {
|
||||
const notRunning: AsideProbe = { ok: false, reason: 'ASIDE_NOT_RUNNING', detail: 'the app is closed' };
|
||||
const noBin = (): null => null;
|
||||
|
||||
Reference in new issue
Block a user