From 0a69bd0b89ac30e7b4b9724e7a70a25b1b6acd51 Mon Sep 17 00:00:00 2001 From: Talal Alqahs <153442159+TFQ0@users.noreply.github.com> Date: Sun, 6 Sep 2026 20:18:02 +0300 Subject: [PATCH 1/4] update --- .github/ISSUE_TEMPLATE/function-request.yml | 59 +++++++++++++++++++ .github/ISSUE_TEMPLATE/improvement.yml | 55 ++++++++++++++++++ .github/ISSUE_TEMPLATE/problem-report.yml | 63 +++++++++++++++++++++ .github/pull_request_template.md | 43 ++++++++++++++ src/domain/profile.ts | 2 +- 5 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 .github/ISSUE_TEMPLATE/function-request.yml create mode 100644 .github/ISSUE_TEMPLATE/improvement.yml create mode 100644 .github/ISSUE_TEMPLATE/problem-report.yml create mode 100644 .github/pull_request_template.md diff --git a/.github/ISSUE_TEMPLATE/function-request.yml b/.github/ISSUE_TEMPLATE/function-request.yml new file mode 100644 index 0000000..7085ab2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/function-request.yml @@ -0,0 +1,59 @@ +name: Function request +description: Propose a new capability or workflow for the Studio. +title: "[Function]: " +body: + - type: markdown + attributes: + value: | + Describe the user need first, then the function that could address it. Please keep proposals consistent with the Studio's privacy and SVG-safety boundaries. + + - type: textarea + id: need + attributes: + label: User need + description: What BUG or workflow would this new function address? + placeholder: As a profile author, I want to... + validations: + required: true + + - type: textarea + id: function + attributes: + label: Proposed function + description: Explain how it should work from a user's perspective. + validations: + required: true + + - type: textarea + id: example + attributes: + label: Example + description: Show a sample input/output, user flow, or acceptance criteria. + placeholder: | + Given... + When... + Then... + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Describe current workarounds or other possible solutions. + + - type: textarea + id: context + attributes: + label: Additional context + description: Add mockups, links to related issues, accessibility needs, security considerations, or compatibility constraints. + + - type: checkboxes + id: checks + attributes: + label: Before submitting + options: + - label: I searched the existing issues for the same or a similar request. + required: true + - label: The request does not require arbitrary CSS, SVG markup, SVG paths, scripts, or remote fonts. + required: true diff --git a/.github/ISSUE_TEMPLATE/improvement.yml b/.github/ISSUE_TEMPLATE/improvement.yml new file mode 100644 index 0000000..af7d188 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/improvement.yml @@ -0,0 +1,55 @@ +name: Improvement proposal +description: Suggest a better version of existing behavior, design, or documentation. +title: "[Improvement]: " +body: + - type: markdown + attributes: + value: | + Use this form when something already exists but could work better. For entirely new behavior, use the function request form. + + - type: textarea + id: current + attributes: + label: Current experience + description: What currently works, and where does it fall short? + placeholder: The export warning identifies the affected field, but it does not explain how to correct it. + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposed improvement + description: Describe the change and include a concrete example when possible. + validations: + required: true + + - type: textarea + id: benefit + attributes: + label: Benefit + description: Who would benefit, and how would this improve their workflow? + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Note any workarounds or other approaches you considered. + + - type: textarea + id: context + attributes: + label: Additional context + description: Add mockups, screenshots, related issues, accessibility considerations, or compatibility constraints. + + - type: checkboxes + id: checks + attributes: + label: Before submitting + options: + - label: I searched the existing issues for a similar proposal. + required: true + - label: This proposal improves existing behavior rather than introducing an entirely new function. + required: true diff --git a/.github/ISSUE_TEMPLATE/problem-report.yml b/.github/ISSUE_TEMPLATE/problem-report.yml new file mode 100644 index 0000000..6079296 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/problem-report.yml @@ -0,0 +1,63 @@ +name: BUG report +description: Report something that is broken or behaves unexpectedly. +title: "[BUG]: " +body: + - type: markdown + attributes: + value: | + Thanks for helping improve Animated GitHub Profile Studio. Please search existing issues before submitting a report. + + - type: textarea + id: BUG + attributes: + label: BUG + description: Describe what went wrong and what you were trying to do. + placeholder: I selected the mobile preview, but the exported SVG... + validations: + required: true + + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + description: Give the smallest reliable sequence that shows the BUG. + placeholder: | + 1. Open the Studio. + 2. Select the Bento Grid template. + 3. Change the preview to mobile and static. + 4. Select Download SVG. + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What should have happened instead? + validations: + required: true + + - type: textarea + id: environment + attributes: + label: Environment + description: Include the browser, operating system, app version or commit, and whether you used the hosted or local Studio. + placeholder: Chrome 140, Windows 11, hosted Studio + validations: + required: true + + - type: textarea + id: evidence + attributes: + label: Additional evidence + description: Add screenshots, exported configuration, console errors, or other useful context. Remove personal or sensitive data first. + + - type: checkboxes + id: checks + attributes: + label: Before submitting + options: + - label: I searched the existing issues for the same BUG. + required: true + - label: I removed tokens, private data, and other sensitive information. + required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..84eb6a8 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,43 @@ +## Summary + + + +Example: Fix mobile static exports so they use the selected responsive composition. + +## Related issue + + + +Closes # + +## Type of change + +- [ ] BUG fix +- [ ] Improvement to existing behavior +- [ ] New function +- [ ] Documentation or maintenance + +## Verification + + + +- [ ] `npm run typecheck` +- [ ] `npm test` +- [ ] `npm run build` +- [ ] Relevant desktop/mobile, dark/light, and animated/static variants were inspected + +Additional verification: + + + +## Screenshots or generated output + + + +## Checklist + +- [ ] I kept this pull request focused and avoided unrelated refactoring. +- [ ] I added or updated tests when behavior changed. +- [ ] I updated documentation when setup, configuration, or user-visible behavior changed. +- [ ] I did not add secrets, private data, unlicensed media, or unsafe raw CSS/SVG input. +- [ ] I preserved backward compatibility, or documented the intentional breaking change above. diff --git a/src/domain/profile.ts b/src/domain/profile.ts index 84db4c5..081f91a 100644 --- a/src/domain/profile.ts +++ b/src/domain/profile.ts @@ -521,7 +521,7 @@ export const defaultProfileConfig: ProfileConfig = { profileLabel: "FICTIONAL / 01", eyebrow: "FICTIONAL SAMPLE", primaryRole: "Creative Developer", - secondaryRole: "Open-source builder / Problem solver", + secondaryRole: "Open-source builder / BUG solver", }, hero: { headline: ["Imagine it.", "Build it.", "Share it."], From 676cb584c665484dd04e1d316726d9a8920aeb67 Mon Sep 17 00:00:00 2001 From: Talal Alqahs <153442159+TFQ0@users.noreply.github.com> Date: Sun, 6 Sep 2026 20:37:13 +0300 Subject: [PATCH 2/4] update --- src/generator/generator.test.ts | 35 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/generator/generator.test.ts b/src/generator/generator.test.ts index 1e0d761..7207fd2 100644 --- a/src/generator/generator.test.ts +++ b/src/generator/generator.test.ts @@ -128,24 +128,25 @@ describe("profile artifact generation", () => { expect(new DOMParser().parseFromString(svg, "image/svg+xml").querySelector("parsererror")).toBeNull(); }); - it("keeps the content surface and footer inside each viewport", () => { + it("keeps the content surface and footer inside the desktop viewport", () => { const config = cloneDefaultConfig(); - - for (const viewport of ["desktop", "mobile"] as const) { - const document = parseSvg(renderHeroSvg(config, { viewport, theme: "dark", motion: "static" })); - const root = document.documentElement; - const surface = document.querySelector('[data-region="surface"]')!; - const footer = document.querySelector('[data-region="footer"]')!; - const width = numericAttribute(root, "width"); - const footerDivider = numericAttribute(root, "data-footer-divider"); - - expect(numericAttribute(surface, "x")).toBe(1); - expect(numericAttribute(surface, "y")).toBe(64); - expect(numericAttribute(surface, "width")).toBe(width - 2); - expect(numericAttribute(surface, "height")).toBe(footerDivider - 64); - expect(footer.querySelectorAll("path")).toHaveLength(1); - expect(footer.querySelectorAll("text")).toHaveLength(2); - } + const document = parseSvg(renderHeroSvg(config, { + viewport: "desktop", + theme: "dark", + motion: "static", + })); + const root = document.documentElement; + const surface = document.querySelector('[data-region="surface"]')!; + const footer = document.querySelector('[data-region="footer"]')!; + const width = numericAttribute(root, "width"); + const footerDivider = numericAttribute(root, "data-footer-divider"); + + expect(numericAttribute(surface, "x")).toBe(1); + expect(numericAttribute(surface, "y")).toBe(64); + expect(numericAttribute(surface, "width")).toBe(width - 2); + expect(numericAttribute(surface, "height")).toBe(footerDivider - 64); + expect(footer.querySelectorAll("path")).toHaveLength(1); + expect(footer.querySelectorAll("text")).toHaveLength(2); }); it("keeps README asset references in sync with the generated files", () => { From a9d30b33862baae72195a5d004e786897af03db6 Mon Sep 17 00:00:00 2001 From: Talal Alqahs <153442159+TFQ0@users.noreply.github.com> Date: Sun, 6 Sep 2026 21:29:12 +0300 Subject: [PATCH 3/4] update --- .github/ISSUE_TEMPLATE/problem-report.yml | 4 +- .github/pull_request_template.md | 4 +- README.md | 20 +- SETUP.md | 10 +- src/App.test.tsx | 22 +- src/App.tsx | 58 ++--- src/components/ProfilePreview.tsx | 4 +- src/domain/profile.ts | 2 +- src/generator/generator.test.ts | 148 +++++------ src/generator/readme.ts | 14 +- src/generator/svg.ts | 284 +++++++-------------- src/styles.css | 287 +--------------------- 12 files changed, 209 insertions(+), 648 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/problem-report.yml b/.github/ISSUE_TEMPLATE/problem-report.yml index 6079296..948f48f 100644 --- a/.github/ISSUE_TEMPLATE/problem-report.yml +++ b/.github/ISSUE_TEMPLATE/problem-report.yml @@ -12,7 +12,7 @@ body: attributes: label: BUG description: Describe what went wrong and what you were trying to do. - placeholder: I selected the mobile preview, but the exported SVG... + placeholder: I selected the static preview, but the exported SVG... validations: required: true @@ -24,7 +24,7 @@ body: placeholder: | 1. Open the Studio. 2. Select the Bento Grid template. - 3. Change the preview to mobile and static. + 3. Change the preview to light and static. 4. Select Download SVG. validations: required: true diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 84eb6a8..ea14a33 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,7 +2,7 @@ -Example: Fix mobile static exports so they use the selected responsive composition. +Example: Fix static exports so they use the selected desktop composition. ## Related issue @@ -24,7 +24,7 @@ Closes # - [ ] `npm run typecheck` - [ ] `npm test` - [ ] `npm run build` -- [ ] Relevant desktop/mobile, dark/light, and animated/static variants were inspected +- [ ] Relevant desktop dark/light and animated/static variants were inspected Additional verification: diff --git a/README.md b/README.md index 8d28c7e..686221b 100644 --- a/README.md +++ b/README.md @@ -12,11 +12,11 @@ After GitHub Pages is enabled, the hosted Studio is available at: Configuration v3 provides seven templates: Quality Control, Classic Terminal, Retro Arcade, Anime HUD, Bento Grid, Signal Poster, and Custom Canvas. The templates use generic, original interface elements and include no franchise characters, logos, or copied artwork. -The templates are more than palette swaps: they provide structurally distinct compositions that adapt independently to desktop and mobile output. Custom Canvas uses the same validated rendering system and offers constrained layout controls rather than unrestricted canvas editing. +The templates are more than palette swaps: they provide structurally distinct desktop compositions. Custom Canvas uses the same validated rendering system and offers constrained layout controls rather than unrestricted canvas editing. The customization contract includes: -- Responsive composition, alignment, spacing, and placement choices within safe layout bounds. +- Desktop composition, alignment, spacing, and placement choices within safe layout bounds. - Two to six workflow steps, with an allowlisted safe shape selected for each step. - Decorative shapes described through a bounded SVG-safe DSL. The renderer converts validated shape data into SVG; users cannot insert arbitrary SVG elements or path commands. - Curated font presets that map to controlled, SVG-safe system font stacks. @@ -28,26 +28,28 @@ The editor does not accept arbitrary raw CSS, SVG markup, SVG paths, or remote f ## What it includes -- Live desktop and mobile preview in dark and light themes. +- Live desktop preview in dark and light themes. - Animated and reduced-motion/static SVG variants. -- Seven structurally distinct responsive templates plus constrained custom layout controls. +- Seven structurally distinct desktop templates plus constrained custom layout controls. - Design, font, profile, hero, workflow-step shape, decorative shape, project, skill, link, structured media, palette, section, Markdown, and footer editing. - Optional public GitHub repository import with no login or token. - Local autosave of the last valid configuration. - Versioned `profile.config.json` import/export. - One-click download of the currently previewed SVG variant without creating a ZIP. -- Deterministic ZIP generation containing `README.md`, setup guidance, the saved config, and all eight SVG assets. +- Deterministic ZIP generation containing `README.md`, setup guidance, the saved config, and all four desktop SVG assets. - Strict runtime validation, HTTPS-only generated links, contextual XML/Markdown escaping, and self-contained SVG output. - WebMCP tools for agents to read or stage the same configuration used by the visible editor. +The Studio targets desktop browser widths of 1180 px or wider. Every generated hero variant uses a fixed 1200 × 610 desktop canvas. + ## Use the studio 1. Open the app and choose one of the seven templates, then adjust its constrained layout controls if desired. 2. Select **Start a blank profile** or customize the clearly labeled fictional sample. 3. Work through Profile, Hero, workflow steps and shapes, Projects, Skills, Links, Media, Colors, and Sections. -4. Check the desktop/mobile, dark/light, and animated/static previews. Responsive compositions can adapt between desktop and mobile rather than preserving identical coordinates. +4. Check the desktop preview in dark/light and animated/static modes. 5. Resolve validation errors and review any design warnings in Export. -6. To keep only the image, choose its desktop/mobile, dark/light, and animated/static options, then select **Download SVG**. +6. To keep only the image, choose its dark/light and animated/static options, then select **Download SVG**. 7. To publish the complete profile, download the ZIP instead. 8. Upload its `README.md` and `assets/` directory to the public GitHub repository whose name exactly matches your username. @@ -104,7 +106,7 @@ src/ ├── domain/profile.ts # strict v3 config plus v1/v2 migration ├── domain/presets.ts # trusted template defaults and visual presets ├── generator/ -│ ├── svg.ts # pure eight-variant SVG renderer +│ ├── svg.ts # pure four-variant desktop SVG renderer │ ├── readme.ts # GitHub README renderer │ ├── escape.ts # XML/Markdown/URL safety boundaries │ └── artifacts.ts # deterministic file and ZIP assembly @@ -114,7 +116,7 @@ src/ └── App.tsx # editor state and workflows ``` -`ProfileConfig` is the single source of truth. The visible preview, README source, SVG files, saved config, and ZIP are all produced from the same validated object. Template, responsive layout, workflow-shape, and decorative-shape values are resolved through trusted renderer registries. Imported GitHub data becomes an editable snapshot; it is never a hidden dependency of later exports. +`ProfileConfig` is the single source of truth. The visible preview, README source, SVG files, saved config, and ZIP are all produced from the same validated object. Template, desktop layout, workflow-shape, and decorative-shape values are resolved through trusted renderer registries. Imported GitHub data becomes an editable snapshot; it is never a hidden dependency of later exports. ## Privacy and output safety diff --git a/SETUP.md b/SETUP.md index ab3bfb1..258b215 100644 --- a/SETUP.md +++ b/SETUP.md @@ -19,12 +19,12 @@ npm test npm run build ``` -These checks cover TypeScript, strict config parsing, all eight SVG variants, reduced-motion output, README-to-asset references, deterministic ZIP contents, unsafe input handling, public GitHub import errors, cache validation, and the WebMCP contract. +These checks cover TypeScript, strict config parsing, all four desktop SVG variants, reduced-motion output, README-to-asset references, deterministic ZIP contents, unsafe input handling, public GitHub import errors, cache validation, and the WebMCP contract. The v3 verification matrix must also cover: - Quality Control, Classic Terminal, Retro Arcade, Anime HUD, Bento Grid, Signal Poster, and Custom Canvas. -- Every supported responsive composition in desktop and mobile output, including constrained alignment, spacing, and placement boundary values. +- Every supported desktop composition, including constrained alignment, spacing, and placement boundary values. - Two-step and six-step workflows, every allowlisted per-step shape, ordering, and text-fit limits. - Decorative shapes at their count and numeric boundaries, plus rejection of unknown primitives, excessive values, raw SVG, arbitrary path data, and CSS injection. - Every curated font preset and rejection of arbitrary font-family values or remote font references. @@ -32,13 +32,13 @@ The v3 verification matrix must also cover: - Structured remote-media validation and attribution, rejection of unsafe media URLs, and confirmation that media remains separate HTTPS README content rather than hero SVG content. - Preservation of profile text, repositories, links, sections, and media when applying a template. Loading a complete sample must remain a separate confirmed replacement action. -For a release, inspect all seven templates with representative and maximum-length content in desktop/mobile, dark/light, and animated/static previews. Confirm that compositions are visibly distinct, remain within the SVG viewBox, and adapt safely between desktop and mobile. Design warnings are advisory because fixed SVG artwork cannot measure browser font metrics during generation. +For a release, inspect all seven templates with representative and maximum-length content in the desktop dark/light and animated/static previews. Confirm that compositions are visibly distinct and remain within the 1200×610 SVG viewBox. Design warnings are advisory because fixed SVG artwork cannot measure browser font metrics during generation. Exercise the constrained custom layout controls at minimum and maximum values, then verify that the visible preview, downloaded current SVG, and matching ZIP asset render the same configuration. Check workflows with two and six steps and mix every safe step shape. Decorative-shape tests must confirm that generated primitives remain bounded and cannot obscure required text or controls. -The preview toolbar can download its currently selected SVG by itself. Use the Export panel's complete ZIP when publishing the full README and all responsive theme variants. +The preview toolbar can download its currently selected SVG by itself. Use the Export panel's complete ZIP when publishing the full README and all desktop theme and motion variants. -When changing the editor layout, inspect 1440 px, 1180 px, 900 px, 620 px, and 320 px viewport widths. Confirm that navigation and export actions remain reachable, keyboard focus remains visible, remote media stays within the preview, and no horizontal page overflow is introduced. +When changing the editor layout, inspect the supported desktop widths at 1440 px and 1180 px. Confirm that navigation and export actions remain reachable, keyboard focus remains visible, remote media stays within the preview, and no horizontal page overflow is introduced. Custom Canvas is constrained despite its name. Do not add arbitrary raw CSS, SVG markup, SVG path input, or remote fonts. Layouts, workflow shapes, and decorative shapes must be selected or composed through validated controls and code-generated renderer primitives. diff --git a/src/App.test.tsx b/src/App.test.tsx index 25fcf68..a4f9e2c 100644 --- a/src/App.test.tsx +++ b/src/App.test.tsx @@ -14,10 +14,6 @@ describe("Profile Studio workspace", () => { configurable: true, value: vi.fn().mockReturnValue({ matches: false }), }); - Object.defineProperty(window, "scrollTo", { - configurable: true, - value: vi.fn(), - }); Object.defineProperty(window, "requestAnimationFrame", { configurable: true, value: (callback: FrameRequestCallback) => { @@ -62,15 +58,17 @@ describe("Profile Studio workspace", () => { ); }); - it("keeps edit and preview panes connected to the responsive switch", () => { - const previewSwitch = Array.from(container.querySelectorAll(".mobile-view-switch button")) - .find((button) => button.textContent === "Preview")!; + it("exposes a desktop-only preview", () => { + const previewOptions = Array.from(container.querySelectorAll(".preview-select > span")) + .map((label) => label.textContent); + const hero = container.querySelector(".generated-hero svg")!; - expect(previewSwitch.getAttribute("aria-controls")).toBe("preview-pane"); - act(() => previewSwitch.click()); - - expect(previewSwitch.getAttribute("aria-pressed")).toBe("true"); - expect(container.querySelector("main")?.classList.contains("mobile-pane-preview")).toBe(true); + expect(previewOptions).toEqual(["Theme", "Motion"]); + expect(container.querySelector(".preview-title strong")?.textContent).toBe( + "desktop / dark / animated", + ); + expect(hero.getAttribute("width")).toBe("1200"); + expect(hero.getAttribute("height")).toBe("610"); expect(container.querySelector("#preview-pane")?.getAttribute("aria-labelledby")).toBe( "preview-heading", ); diff --git a/src/App.tsx b/src/App.tsx index 37180cd..c9333b0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -38,7 +38,6 @@ import type { HeroMotion, HeroTheme, HeroVariant, - HeroViewport, } from "./generator/svg"; import { fetchPublicRepositories, @@ -353,28 +352,28 @@ function contrastRatio(first: string, second: string): number | null { function getWarnings(config: ProfileConfig): string[] { const warnings: string[] = []; if (config.hero.headline.some((line) => line.length > 18)) { - warnings.push("A headline line may be tight on the mobile header."); + warnings.push("A headline line may be tight in compact desktop compositions."); } if (config.hero.command.length > 24) { - warnings.push("The terminal command may be clipped on smaller layouts."); + warnings.push("The terminal command may be clipped in narrow terminal panels."); } if (config.hero.checks.some((check) => check.length > 18)) { - warnings.push("A check label may overlap its status on mobile."); + warnings.push("A check label may overlap its status in narrow terminal panels."); } if (config.identity.secondaryRole.length > 40) { - warnings.push("The secondary role may be tight on mobile."); + warnings.push("The secondary role may be tight in narrower desktop compositions."); } if (config.identity.brandMark.length > 7 || config.identity.headerLabel.length > 20) { warnings.push("The header mark and label may compete for space."); } if (config.identity.profileLabel.length > 14) { - warnings.push("The profile badge may be tight in the mobile header."); + warnings.push("The profile badge may be tight in the header."); } if (config.identity.eyebrow.length > 24) { - warnings.push("The eyebrow name may be tight on mobile."); + warnings.push("The eyebrow name may be tight in the identity panel."); } if (config.hero.footerLeft.length > 24 || config.hero.footerRight.length > 34) { - warnings.push("A hero footer label may be clipped on mobile."); + warnings.push("A hero footer label may be clipped in its desktop column."); } const gifsWithoutFallback = config.media.filter( (item) => item.kind === "gif" && !item.reducedMotionUrl, @@ -434,9 +433,7 @@ function sanitizedDownloadName(username: string): string { export default function App() { const [config, setConfig] = useState(loadInitialConfig); const [activePanel, setActivePanel] = useState("design"); - const [mobilePane, setMobilePane] = useState<"edit" | "preview">("edit"); const [theme, setTheme] = useState("dark"); - const [viewport, setViewport] = useState("desktop"); const [motion, setMotion] = useState(() => window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "static" : "animated", ); @@ -453,7 +450,6 @@ export default function App() { const [showArchived, setShowArchived] = useState(false); const [designUndo, setDesignUndo] = useState(null); const configFileInput = useRef(null); - const editorPane = useRef(null); const editorScroll = useRef(null); const stepRail = useRef(null); const importAbortController = useRef(null); @@ -477,7 +473,7 @@ export default function App() { ], [config.sections], ); - const variant: HeroVariant = { theme, viewport, motion }; + const variant: HeroVariant = { theme, motion }; const activePanelIndex = panels.findIndex((panel) => panel.key === activePanel); const activePanelDetails = panels[activePanelIndex] ?? panels[0]!; @@ -489,20 +485,6 @@ export default function App() { stepRail.current ?.querySelector(`#step-${panel}`) ?.scrollIntoView({ block: "nearest", inline: "nearest" }); - if (window.matchMedia("(max-width: 1050px)").matches) { - const switchHeight = document.querySelector(".mobile-view-switch")?.offsetHeight ?? 0; - const railHeight = stepRail.current?.offsetHeight ?? 0; - const editorTop = editorPane.current?.offsetTop ?? 0; - window.scrollTo({ top: Math.max(0, editorTop - switchHeight - railHeight) }); - } - }); - } - - function selectMobilePane(pane: "edit" | "preview") { - setMobilePane(pane); - window.requestAnimationFrame(() => { - if (window.matchMedia("(max-width: 1050px)").matches) window.scrollTo({ top: 0 }); - document.getElementById(pane === "edit" ? "editor-heading" : "preview-heading")?.focus(); }); } @@ -939,12 +921,7 @@ export default function App() { -
- - -
- -
+
-