mirror of
https://github.com/jason5ng32/MyIP.git
synced 2026-11-04 08:55:47 +00:00
A pass over every file the branch touched, after the iOS animation hunt left tries and reverts behind it. Removed what nothing reaches: the view-transition rule for a transition no code starts, an unreachable guard in the transition hook, a no-op KeepAlive key branch, a sign-in guard the menu already enforces, usage-dialog wrappers that only forwarded to the store, a duplicated modifier-click check and card filter (isPlainClick / listedTools), a test that hand-rolled listedTools, a drawer-era fixture parameter. auth-flow.js folded into auth-redirect.js. Comments cut to what the code cannot say itself — file headers of a few lines, the measured iOS reasons, non-obvious orderings — and stale ones fixed (Nav under KeepAlive, Home.vue reusing DEFAULT_META, "from Nav", enabledCards). AGENTS.md sections tightened to the code as it is now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013GbLTGMZ45MV78qd2tHoco
67 lines
2.6 KiB
JavaScript
67 lines
2.6 KiB
JavaScript
// Per-route document head (title / description / canonical / Open Graph).
|
|
//
|
|
// There's no SSR, so this runs client-side: each routed page (Home.vue, the
|
|
// tool page, /privacy, /r/:id) calls useDocumentMeta() with its own values.
|
|
// Only the active route writes, so a kept-alive page re-applies its head on
|
|
// return. Reactive: locale switches and slug changes re-apply automatically.
|
|
//
|
|
// index.html ships sensible homepage defaults (used on first paint and as the
|
|
// fallback here); this just overrides them once Vue is driving the page.
|
|
|
|
import { watchEffect } from 'vue';
|
|
import { useRouteActive } from './use-route-active.js';
|
|
|
|
// Snapshot the index.html-provided values once, before any route overrides
|
|
// them: the fallback for fields a page leaves out.
|
|
export const DEFAULT_META = {
|
|
title: document.title,
|
|
description: document.head.querySelector('meta[name="description"]')?.getAttribute('content') || '',
|
|
};
|
|
|
|
// Create-or-update a <meta> tag keyed by name= or property=.
|
|
function setMeta(key, attr, content) {
|
|
if (content == null) return;
|
|
let el = document.head.querySelector(`meta[${attr}="${key}"]`);
|
|
if (!el) {
|
|
el = document.createElement('meta');
|
|
el.setAttribute(attr, key);
|
|
document.head.appendChild(el);
|
|
}
|
|
el.setAttribute('content', content);
|
|
}
|
|
|
|
// Create-or-update a <link rel="..."> tag.
|
|
function setLink(rel, href) {
|
|
if (!href) return;
|
|
let el = document.head.querySelector(`link[rel="${rel}"]`);
|
|
if (!el) {
|
|
el = document.createElement('link');
|
|
el.setAttribute('rel', rel);
|
|
document.head.appendChild(el);
|
|
}
|
|
el.setAttribute('href', href);
|
|
}
|
|
|
|
// `getMeta` returns { title?, description?, canonical? }. Missing fields fall
|
|
// back to the homepage defaults / the current origin. Read reactive sources
|
|
// (t(), route params) inside it so the head tracks them.
|
|
export function useDocumentMeta(getMeta) {
|
|
const active = useRouteActive();
|
|
watchEffect(() => {
|
|
if (!active.value) return;
|
|
const meta = getMeta() || {};
|
|
const title = meta.title || DEFAULT_META.title;
|
|
const description = meta.description || DEFAULT_META.description;
|
|
const canonical = meta.canonical || `${window.location.origin}/`;
|
|
|
|
document.title = title;
|
|
setMeta('description', 'name', description);
|
|
setLink('canonical', canonical);
|
|
setMeta('og:url', 'property', canonical);
|
|
setMeta('og:title', 'property', title);
|
|
setMeta('og:description', 'property', description);
|
|
setMeta('twitter:title', 'name', title);
|
|
setMeta('twitter:description', 'name', description);
|
|
});
|
|
}
|