@joystyk/catalog 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +29 -2
  3. package/dist/advertise.d.ts +82 -0
  4. package/dist/advertise.js +203 -0
  5. package/dist/advertise.js.map +1 -0
  6. package/dist/categories.d.ts +12 -0
  7. package/dist/categories.js +45 -0
  8. package/dist/categories.js.map +1 -0
  9. package/dist/client.d.ts +1 -0
  10. package/dist/client.js +9 -0
  11. package/dist/client.js.map +1 -0
  12. package/dist/components/advertise.d.ts +4 -0
  13. package/dist/components/advertise.js +1304 -0
  14. package/dist/components/advertise.js.map +1 -0
  15. package/dist/components/app.d.ts +25 -0
  16. package/dist/components/app.js +106 -0
  17. package/dist/components/app.js.map +1 -0
  18. package/dist/components/leaving.d.ts +2 -0
  19. package/dist/components/leaving.js +104 -0
  20. package/dist/components/leaving.js.map +1 -0
  21. package/dist/components/views.d.ts +13 -0
  22. package/dist/components/views.js +1410 -0
  23. package/dist/components/views.js.map +1 -0
  24. package/dist/head.d.ts +6 -0
  25. package/dist/head.js +33 -0
  26. package/dist/head.js.map +1 -0
  27. package/dist/index.d.ts +11 -0
  28. package/dist/index.js +12 -0
  29. package/dist/labels.d.ts +10 -0
  30. package/dist/labels.js +27 -0
  31. package/dist/labels.js.map +1 -0
  32. package/dist/placeholders.d.ts +3 -0
  33. package/dist/placeholders.js +7 -0
  34. package/dist/placeholders.js.map +1 -0
  35. package/dist/registry.d.ts +46 -0
  36. package/dist/registry.js +106 -0
  37. package/dist/registry.js.map +1 -0
  38. package/dist/routes.d.ts +56 -0
  39. package/dist/routes.js +89 -0
  40. package/dist/routes.js.map +1 -0
  41. package/dist/seo.d.ts +31 -0
  42. package/dist/seo.js +174 -0
  43. package/dist/seo.js.map +1 -0
  44. package/dist/server.d.ts +50 -0
  45. package/dist/server.js +162 -0
  46. package/dist/server.js.map +1 -0
  47. package/dist/store.d.ts +202 -0
  48. package/dist/store.js +136 -0
  49. package/dist/store.js.map +1 -0
  50. package/dist/styles.css +1121 -0
  51. package/dist/wire.d.ts +108 -0
  52. package/dist/wire.js +74 -0
  53. package/dist/wire.js.map +1 -0
  54. package/package.json +63 -11
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advertise.js","names":["_gyral$compiled"],"sources":["../../src/components/advertise.ts"],"sourcesContent":["// The \"Advertise with us\" pages: the slots, the audience, rates, the creative rules and the\n// contact form (monetization.md, \"How a campaign works\", step 1), the creative policy, and\n// where a sent form lands. The form is a plain POST to the site's contact handler, so it\n// works without JavaScript; a rejected post comes back as this page with the visitor's values\n// and the errors, seeded into the catalog store (`contactForm`). List rows are module-level\n// and pure (Gyral's `each` rule).\nimport { each, html, invalid, nothing } from '@gyral/core';\nimport { SLOTS, type SlotDefinition, type TargetingKey } from '@joystyk/ui/slots';\nimport {\n ADVERTISE_EMAIL,\n CLAIMS_RULES,\n CONTACT_HANDLED_RETENTION_DAYS,\n CONTACT_HONEYPOT,\n CONTACT_LIMITS,\n CONTACT_RETENTION_MONTHS,\n FORMAT_RULES,\n LANDING_RULES,\n PROHIBITED,\n REFUND_RULES,\n REPLY_BUSINESS_DAYS,\n RESTRICTED,\n REVIEW_STEPS,\n RULE_SUMMARY,\n SLOT_NAMES,\n SURFACE_NAMES,\n previewSize,\n sizeLabel,\n type PolicyItem,\n} from '../advertise.ts';\nimport { ADVERTISE_PATH, ADVERTISE_POLICY_PATH } from '../routes.ts';\nimport type { CatalogState } from '../store.ts';\n\nconst TARGET_NAMES: Readonly<Record<TargetingKey, string>> = {\n page: 'page',\n game: 'game',\n category: 'category',\n date: 'date',\n};\n\n/** \"a, b and c\". */\nconst listWords = (words: readonly string[]): string =>\n words.length < 2 ? (words[0] ?? '') : `${words.slice(0, -1).join(', ')} and ${words.at(-1) ?? ''}`;\n\nconst capitalize = (text: string): string => `${text.charAt(0).toUpperCase()}${text.slice(1)}`;\n\n/** Previews are drawn at a third of the slot's size, so they compare with each other. */\nconst PREVIEW_SCALE = 3;\n\n// --- The slot gallery --------------------------------------------------------------------\n\nconst SlotCard = (s: SlotDefinition) => {\n const box = previewSize(s);\n return html`<li>\n <article class=\"slot-card\" aria-labelledby=\"slot-${s.id}\">\n <h3 id=\"slot-${s.id}\">${SLOT_NAMES[s.id]}</h3>\n <div class=\"slot-preview\">\n <svg\n class=\"slot-shape\"\n width=${Math.round(box.width / PREVIEW_SCALE)}\n height=${Math.round(box.height / PREVIEW_SCALE)}\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <rect width=\"100%\" height=\"100%\" rx=\"3\" />\n </svg>\n </div>\n <p>${s.placement}</p>\n <dl class=\"slot-facts\">\n <div><dt>Appears on</dt><dd>${capitalize(listWords(s.surfaces.map((x) => SURFACE_NAMES[x])))}</dd></div>\n <div><dt>Wide screens</dt><dd>${s.sizes.wide === null ? 'Not shown' : sizeLabel(s.sizes.wide)}</dd></div>\n <div><dt>Narrow screens</dt><dd>${s.sizes.narrow === null ? 'Not shown' : sizeLabel(s.sizes.narrow)}</dd></div>\n <div><dt>Label</dt><dd>${s.label}</dd></div>\n <div><dt>Targeting</dt><dd>${capitalize(listWords(s.targeting.map((t) => TARGET_NAMES[t])))}</dd></div>\n </dl>\n </article>\n </li>`;\n};\n\nconst gallery = () => html`<section class=\"advertise-section\" aria-labelledby=\"slots-heading\">\n <h2 id=\"slots-heading\">The ad slots</h2>\n <p>\n Each slot has a fixed size on wide and narrow screens, so your ad appears exactly as you made it and the page never\n jumps around it. The shapes below are drawn to scale.\n </p>\n <ul class=\"slot-gallery\" role=\"list\">\n ${each(SLOTS, (s) => s.id, SlotCard)}\n </ul>\n</section>`;\n\n// --- Audience, rates and rules -----------------------------------------------------------\n\nconst audience = () => html`<section class=\"advertise-section\" aria-labelledby=\"audience-heading\">\n <h2 id=\"audience-heading\">Who sees your ad</h2>\n <ul class=\"points\">\n <li><strong>Every age.</strong> Joystyk is for families, children and grown-ups alike, so every ad must suit all of them.</li>\n <li>\n <strong>Chosen by context, never by person.</strong> We pick an ad by the page, game, category and date. Everyone on\n the same page on the same day sees the same ad.\n </li>\n <li>\n <strong>No tracking.</strong> No cookies, profiles or third-party ad scripts. We never learn who saw your ad, and\n neither do you.\n </li>\n <li><strong>Counted on our server.</strong> We report impressions and clicks per creative per day.</li>\n </ul>\n <p>We don't publish traffic figures yet. Once we have a season of data, we'll share them on request.</p>\n</section>`;\n\nconst rates = () => html`<section class=\"advertise-section\" aria-labelledby=\"rates-heading\">\n <h2 id=\"rates-heading\">Rates</h2>\n <p>\n Contact us for rates. We price each booking by slot and dates: the home page banner sells by the day or the week, and\n the daily presenting sponsor by the day. We bill by invoice.\n </p>\n</section>`;\n\nconst Point = (text: string) => html`<li>${text}</li>`;\n\nconst rules = () => html`<section class=\"advertise-section\" aria-labelledby=\"rules-heading\">\n <h2 id=\"rules-heading\">The rules every ad meets</h2>\n <p>Children see these slots, so we hold every ad to children's advertising standards. In short:</p>\n <ul class=\"points\">\n ${each(RULE_SUMMARY, (r) => r, Point)}\n </ul>\n <p><a href=${ADVERTISE_POLICY_PATH}>Read the full creative policy</a>, with the image specs for each slot.</p>\n</section>`;\n\n// --- The contact form --------------------------------------------------------------------\n\ntype Errors = Readonly<Record<string, readonly string[]>>;\n\nconst text = (c: CatalogState, name: string): string => {\n const value = c.contactForm?.values[name];\n return typeof value === 'string' ? value : '';\n};\n\nconst chosenSlots = (c: CatalogState): readonly string[] => {\n const value = c.contactForm?.values['slots'];\n return value === undefined ? [] : typeof value === 'string' ? [value] : value;\n};\n\n/** The ids an input's aria-describedby names: its hint and its error, when present. */\nconst describedBy = (id: string, hint: boolean, errors: readonly string[] | undefined): string | null => {\n const ids = [...(hint ? [`${id}-hint`] : []), ...(errors === undefined ? [] : [`${id}-error`])];\n return ids.length === 0 ? null : ids.join(' ');\n};\n\nconst fieldError = (id: string, errors: readonly string[] | undefined) =>\n errors === undefined ? nothing : html`<p class=\"field-error\" id=\"${id}-error\">${errors.join(' ')}</p>`;\n\ninterface TextField {\n readonly name: 'name' | 'company' | 'email';\n readonly label: string;\n readonly type: 'text' | 'email';\n readonly autocomplete: string;\n readonly required: boolean;\n}\n\nconst TEXT_FIELDS: readonly TextField[] = [\n { name: 'name', label: 'Your name', type: 'text', autocomplete: 'name', required: true },\n { name: 'company', label: 'Company or organization', type: 'text', autocomplete: 'organization', required: false },\n { name: 'email', label: 'Email address', type: 'email', autocomplete: 'email', required: true },\n];\n\nconst optional = html` <span class=\"optional\">(optional)</span>`;\n\nconst textField = (f: TextField, value: string, errors: readonly string[] | undefined) => {\n const id = `contact-${f.name}`;\n return html`<div class=\"field\">\n <label for=${id}>${f.label}${f.required ? nothing : optional}</label>\n ${fieldError(id, errors)}\n <input\n id=${id}\n name=${f.name}\n type=${f.type}\n autocomplete=${f.autocomplete}\n maxlength=${CONTACT_LIMITS[f.name]}\n ?required=${f.required}\n value=${value}\n aria-describedby=${describedBy(id, false, errors)}\n ${invalid(errors)}\n />\n </div>`;\n};\n\nconst SlotChoice = (s: SlotDefinition, checked: boolean) => html`<li>\n <label class=\"choice\">\n <input type=\"checkbox\" name=\"slots\" value=${s.id} ?checked=${checked} />\n ${SLOT_NAMES[s.id]}\n </label>\n</li>`;\n\ninterface Problem {\n readonly field: string;\n readonly message: string;\n}\n\n/** Where each field's error links to in the summary. */\nconst FIELD_TARGETS: Readonly<Record<string, string>> = {\n name: 'contact-name',\n company: 'contact-company',\n email: 'contact-email',\n slots: 'contact-slots',\n start: 'contact-start',\n end: 'contact-end',\n message: 'contact-message',\n};\n\nconst ProblemItem = (p: Problem) => {\n const target = FIELD_TARGETS[p.field];\n return html`<li>${target === undefined ? p.message : html`<a href=\"#${target}\">${p.message}</a>`}</li>`;\n};\n\nconst problems = (errors: Errors): readonly Problem[] =>\n ['', ...Object.keys(FIELD_TARGETS)].flatMap((field) => (errors[field] ?? []).map((message) => ({ field, message })));\n\nconst errorSummary = (errors: Errors) => {\n const list = problems(errors);\n return list.length === 0\n ? nothing\n : html`<div class=\"form-errors\" role=\"alert\" aria-labelledby=\"form-errors-heading\">\n <h3 id=\"form-errors-heading\">Your message wasn't sent</h3>\n <ul>\n ${each(list, (p) => `${p.field}:${p.message}`, ProblemItem)}\n </ul>\n </div>`;\n};\n\n/** How the form's messages are used and kept (advertising-policy.md, \"Contact form messages\"). */\nconst PRIVACY_NOTE =\n 'We use what you send only to reply to you about advertising. We never add you to a mailing list or share your ' +\n `details. We delete each message ${String(CONTACT_HANDLED_RETENTION_DAYS)} days after we've dealt with it, and ` +\n `always within ${String(CONTACT_RETENTION_MONTHS)} months of its arrival. This form is for businesses: if you're ` +\n \"under 18, please don't use it.\";\n\n/** The address, as text anyone can select and copy, and as a mailto: link for those whose mail app opens. */\nconst emailLink = () => html`<a href=${`mailto:${ADVERTISE_EMAIL}`}>${ADVERTISE_EMAIL}</a>`;\n\nconst contactForm = (c: CatalogState) => {\n const errors: Errors = c.contactForm?.errors ?? {};\n const chosen = chosenSlots(c);\n const isChosen = (s: SlotDefinition) => chosen.includes(s.id);\n return html`<section class=\"advertise-section contact\" aria-labelledby=\"contact-heading\">\n <h2 id=\"contact-heading\">Get in touch</h2>\n <p>\n Tell us what you'd like to advertise and when. We'll reply by email within ${REPLY_BUSINESS_DAYS} business days, with\n availability and rates. You can also write to us at ${emailLink()}.\n </p>\n <form class=\"contact-form\" method=\"post\" action=${ADVERTISE_PATH} aria-labelledby=\"contact-heading\">\n ${errorSummary(errors)}\n ${TEXT_FIELDS.map((f) => textField(f, text(c, f.name), errors[f.name]))}\n <fieldset class=\"field\" id=\"contact-slots\" aria-describedby=${describedBy('contact-slots', false, errors['slots'])}>\n <legend>Slots you're interested in${optional}</legend>\n ${fieldError('contact-slots', errors['slots'])}\n <ul class=\"choices\" role=\"list\">\n ${each(SLOTS, (s) => s.id, SlotChoice, isChosen)}\n </ul>\n </fieldset>\n <fieldset class=\"field dates\" aria-describedby=\"contact-dates-hint\">\n <legend>Dates${optional}</legend>\n <p class=\"hint\" id=\"contact-dates-hint\">The first and last day you'd like your ad to run.</p>\n <div class=\"date-pair\">\n <div class=\"field\">\n <label for=\"contact-start\">First day</label>\n ${fieldError('contact-start', errors['start'])}\n <input\n id=\"contact-start\"\n name=\"start\"\n type=\"date\"\n value=${text(c, 'start')}\n aria-describedby=${describedBy('contact-start', false, errors['start'])}\n ${invalid(errors['start'])}\n />\n </div>\n <div class=\"field\">\n <label for=\"contact-end\">Last day</label>\n ${fieldError('contact-end', errors['end'])}\n <input\n id=\"contact-end\"\n name=\"end\"\n type=\"date\"\n value=${text(c, 'end')}\n aria-describedby=${describedBy('contact-end', false, errors['end'])}\n ${invalid(errors['end'])}\n />\n </div>\n </div>\n </fieldset>\n <div class=\"field\">\n <label for=\"contact-message\">Message</label>\n <p class=\"hint\" id=\"contact-message-hint\">What you'd like to advertise, and anything else we should know.</p>\n ${fieldError('contact-message', errors['message'])}\n <textarea\n id=\"contact-message\"\n name=\"message\"\n rows=\"6\"\n maxlength=${CONTACT_LIMITS.message}\n required\n aria-describedby=${describedBy('contact-message', true, errors['message'])}\n ${invalid(errors['message'])}\n >${text(c, 'message')}</textarea>\n </div>\n <div class=\"trap\">\n <label for=\"contact-${CONTACT_HONEYPOT}\">Leave this field empty</label>\n <input id=\"contact-${CONTACT_HONEYPOT}\" name=${CONTACT_HONEYPOT} type=\"text\" tabindex=\"-1\" autocomplete=\"off\" />\n </div>\n <p class=\"form-note\">${PRIVACY_NOTE}</p>\n <p><button type=\"submit\" class=\"cta\">Send message</button></p>\n </form>\n </section>`;\n};\n\nexport function advertiseView(c: CatalogState) {\n return html`<div class=\"advertise-page\">\n <header class=\"page-head\">\n <p class=\"kicker\">Advertise</p>\n <h1 tabindex=\"-1\">Advertise with us</h1>\n <p class=\"lede\">\n Joystyk sells its own ad space, like billboards beside a road. Your ad sits in a fixed, labeled slot beside free games\n for every age: never inside a game and never during play.\n </p>\n </header>\n ${audience()} ${gallery()} ${rates()} ${rules()} ${contactForm(c)}\n </div>`;\n}\n\n// --- The creative policy -----------------------------------------------------------------\n\nconst PolicyEntry = (p: PolicyItem) => html`<div>\n <dt>${p.name}</dt>\n <dd>${p.detail}</dd>\n</div>`;\n\nconst SpecRow = (s: SlotDefinition) => html`<tr>\n <th scope=\"row\">${SLOT_NAMES[s.id]}</th>\n <td>${s.sizes.wide === null ? 'Not shown' : sizeLabel(s.sizes.wide)}</td>\n <td>${s.sizes.narrow === null ? 'Not shown' : sizeLabel(s.sizes.narrow)}</td>\n <td>${s.label}</td>\n</tr>`;\n\nconst Step = (text: string) => html`<li>${text}</li>`;\n\nexport function policyView() {\n return html`<article class=\"advertise-page policy-page\" aria-labelledby=\"policy-title\">\n <nav class=\"crumbs\" aria-label=\"Breadcrumb\">\n <ol>\n <li><a href=${ADVERTISE_PATH}>Advertise with us</a></li>\n <li><a href=${ADVERTISE_POLICY_PATH} aria-current=\"page\">Creative policy</a></li>\n </ol>\n </nav>\n <header class=\"page-head\">\n <p class=\"kicker\">For advertisers</p>\n <h1 id=\"policy-title\" tabindex=\"-1\">Creative policy</h1>\n <p class=\"lede\">\n Children play on Joystyk, so every ad must suit every age. These rules follow children's advertising standards in the\n US and the UK, and every creative meets them before it runs.\n </p>\n </header>\n <section class=\"advertise-section\" aria-labelledby=\"prohibited-heading\">\n <h2 id=\"prohibited-heading\">Products we never advertise</h2>\n <dl class=\"policy-list\">\n ${each(PROHIBITED, (p) => p.name, PolicyEntry)}\n </dl>\n </section>\n <section class=\"advertise-section\" aria-labelledby=\"restricted-heading\">\n <h2 id=\"restricted-heading\">Products we advertise with conditions</h2>\n <dl class=\"policy-list\">\n ${each(RESTRICTED, (p) => p.name, PolicyEntry)}\n </dl>\n </section>\n <section class=\"advertise-section\" aria-labelledby=\"claims-heading\">\n <h2 id=\"claims-heading\">Honest ads, no pressure</h2>\n <ul class=\"points\">\n ${each(CLAIMS_RULES, (r) => r, Point)}\n </ul>\n </section>\n <section class=\"advertise-section\" aria-labelledby=\"labels-heading\">\n <h2 id=\"labels-heading\">Labels</h2>\n <p>\n Every slot carries a label we add: “Advertisement”, or “Sponsored” for the daily presenting sponsor. Your creative\n must not hide the label, imitate it or call itself anything else.\n </p>\n </section>\n <section class=\"advertise-section\" aria-labelledby=\"landing-heading\">\n <h2 id=\"landing-heading\">Landing pages</h2>\n <ul class=\"points\">\n ${each(LANDING_RULES, (r) => r, Point)}\n </ul>\n </section>\n <section class=\"advertise-section\" aria-labelledby=\"specs-heading\">\n <h2 id=\"specs-heading\">Image specs</h2>\n <ul class=\"points\">\n ${each(FORMAT_RULES, (r) => r, Point)}\n </ul>\n <div class=\"table-scroll\" role=\"region\" aria-labelledby=\"specs-caption\" tabindex=\"0\">\n <table class=\"spec-table\">\n <caption id=\"specs-caption\">The box each slot reserves, in CSS pixels</caption>\n <thead>\n <tr>\n <th scope=\"col\">Slot</th>\n <th scope=\"col\">Wide screens</th>\n <th scope=\"col\">Narrow screens</th>\n <th scope=\"col\">Label</th>\n </tr>\n </thead>\n <tbody>\n ${each(SLOTS, (s) => s.id, SpecRow)}\n </tbody>\n </table>\n </div>\n </section>\n <section class=\"advertise-section\" aria-labelledby=\"review-heading\">\n <h2 id=\"review-heading\">Review</h2>\n <ol class=\"steps\">\n ${each(REVIEW_STEPS, (r) => r, Step)}\n </ol>\n <p>\n We may refuse any creative, and take down any ad at any time, if it breaks these rules or we judge it unsuitable for\n a young audience. When we take an ad down, we tell you why.\n </p>\n </section>\n <section class=\"advertise-section\" aria-labelledby=\"refunds-heading\">\n <h2 id=\"refunds-heading\">If an ad doesn't run</h2>\n <ul class=\"points\">\n ${each(REFUND_RULES, (r) => r, Point)}\n </ul>\n </section>\n <p><a href=${`${ADVERTISE_PATH}#contact-heading`}>Ask us about advertising</a></p>\n </article>`;\n}\n\n// --- After a post ------------------------------------------------------------------------\n\nexport const sentView = () => html`<section class=\"sent\" aria-labelledby=\"sent-heading\">\n <p class=\"kicker\">Message sent</p>\n <h1 id=\"sent-heading\" tabindex=\"-1\">Thanks for getting in touch</h1>\n <p class=\"lede\">We read every message about advertising, and we'll reply by email within ${REPLY_BUSINESS_DAYS} business days.</p>\n <p>To add something to your message, write to us at ${emailLink()}.</p>\n <ul class=\"sent-links\">\n <li><a href=${ADVERTISE_PATH}>Back to Advertise with us</a></li>\n <li><a href=\"/\">See all games</a></li>\n </ul>\n</section>`;\n"],"mappings":"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IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;GAAA,CAAA,YAAA,EAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;GAAA,CAAA,YAAA,EAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;CAAA;CAAA,YAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA,EAAA,KAAA,OAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA,EAAA,KAAA,OAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;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"}
@@ -0,0 +1,25 @@
1
+ import { type Command } from '@gyral/core';
2
+ import { type RouteLocation } from '@gyral/router';
3
+ import { type Page } from '../routes.js';
4
+ import { type CatalogState } from '../store.js';
5
+ export interface AppState {
6
+ readonly page: Page;
7
+ /** A navigation happened and its content isn't on screen yet: focus the h1 when it is. */
8
+ readonly focusPending: boolean;
9
+ /** True once hydrated; exposed to CSS and tests as `jy-app:state(live)`. */
10
+ readonly live: boolean;
11
+ }
12
+ export type AppMsg = {
13
+ readonly _tag: 'Routed';
14
+ readonly location: RouteLocation;
15
+ };
16
+ /** Commands that make sure the catalog holds what `page` shows. */
17
+ export declare function loadFor(page: Page, c: CatalogState): Command<AppMsg>[];
18
+ export declare const App: import("@gyral/core").GyralElementClass<AppState, AppMsg, {
19
+ readonly path: string;
20
+ }, never, never>;
21
+ declare global {
22
+ interface HTMLElementTagNameMap {
23
+ 'jy-app': InstanceType<typeof App>;
24
+ }
25
+ }
@@ -0,0 +1,106 @@
1
+ import { pageFor, samePage } from "../routes.js";
2
+ import { headFor } from "../seo.js";
3
+ import { toGyralHead } from "../head.js";
4
+ import { catalog } from "../store.js";
5
+ import { footer, isReady, mainView, masthead } from "./views.js";
6
+ import { compiled } from "@gyral/core/compiled";
7
+ import { changed, define, focus, prop, send } from "@gyral/core";
8
+ import { listen, setHead } from "@gyral/router";
9
+ //#region src/components/app.ts
10
+ var _gyral$t0 = {
11
+ "id": "9n5c5zulmm",
12
+ "html": "<main id=\"main\" tabindex=\"-1\"></main>",
13
+ "parts": [
14
+ [
15
+ 2,
16
+ [],
17
+ 0
18
+ ],
19
+ [1, [0]],
20
+ [0, []]
21
+ ],
22
+ "segments": [
23
+ { "k": "child" },
24
+ "<main id=\"main\" tabindex=\"-1\">",
25
+ {
26
+ "k": "child",
27
+ "in": "main"
28
+ },
29
+ "</main>",
30
+ { "k": "child" }
31
+ ]
32
+ };
33
+ var props = { path: prop.string({ required: true }) };
34
+ /** Commands that make sure the catalog holds what `page` shows. */
35
+ function loadFor(page, c) {
36
+ if ((page._tag === "Home" || page._tag === "Category") && c.summaries === null) return [send(catalog, { _tag: "LoadSummaries" })];
37
+ if (page._tag === "Game") {
38
+ const commands = [];
39
+ if (!(page.id in c.games)) commands.push(send(catalog, {
40
+ _tag: "LoadGame",
41
+ id: page.id
42
+ }));
43
+ if (c.summaries === null) commands.push(send(catalog, { _tag: "LoadSummaries" }));
44
+ return commands;
45
+ }
46
+ return [];
47
+ }
48
+ var focusHeading = () => focus("main h1");
49
+ var App = define()("jy-app", {
50
+ shadow: false,
51
+ props,
52
+ stores: [catalog],
53
+ init: (p) => ({
54
+ page: pageFor(p.path),
55
+ focusPending: false,
56
+ live: false
57
+ }),
58
+ subscriptions: () => [listen((location) => ({
59
+ _tag: "Routed",
60
+ location
61
+ }))],
62
+ states: (s) => ({ live: s.live }),
63
+ intent: {},
64
+ update: {
65
+ Routed: (s, m, { read }) => {
66
+ const page = pageFor(m.location.pathname);
67
+ if (samePage(page, s.page)) return s;
68
+ const c = read(catalog);
69
+ const ready = isReady(page, c);
70
+ return [{
71
+ ...s,
72
+ page,
73
+ focusPending: !ready
74
+ }, [
75
+ ...loadFor(page, c),
76
+ setHead(toGyralHead(headFor(page, c))),
77
+ ...ready ? [focusHeading()] : []
78
+ ]];
79
+ },
80
+ Hydrated: (s) => ({
81
+ ...s,
82
+ live: true
83
+ }),
84
+ StoreChanged: (s, m) => {
85
+ const c = changed(catalog, m);
86
+ if (c === void 0) return s;
87
+ const ready = s.focusPending && isReady(s.page, c.state);
88
+ return [{
89
+ ...s,
90
+ focusPending: s.focusPending && !ready
91
+ }, [setHead(toGyralHead(headFor(s.page, c.state))), ...ready ? [focusHeading()] : []]];
92
+ }
93
+ },
94
+ view: (s, _i, { read }) => {
95
+ const c = read(catalog);
96
+ return compiled(_gyral$t0, [
97
+ masthead(s.page, c),
98
+ mainView(s.page, c),
99
+ footer()
100
+ ]);
101
+ }
102
+ });
103
+ //#endregion
104
+ export { App, loadFor };
105
+
106
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","names":["_gyral$compiled"],"sources":["../../src/components/app.ts"],"sourcesContent":["// <jy-app>: the page shell. Light DOM (`shadow: false`), so crawlers and the document\n// stylesheet see the content. It owns the page, so its router captures same-origin link\n// clicks. The server renders it with `path`; in the browser `listen` reports the same URL\n// first (ignored), then every navigation. After a navigation it loads what the new page\n// needs, updates the head, and moves focus to the new page's h1 so keyboard and screen\n// reader users land on the content.\nimport { changed, define, focus, html, prop, send, type Command } from '@gyral/core';\nimport { listen, setHead, type RouteLocation } from '@gyral/router';\nimport { toGyralHead } from '../head.ts';\nimport { pageFor, samePage, type Page } from '../routes.ts';\nimport { headFor } from '../seo.ts';\nimport { catalog, type CatalogState } from '../store.ts';\nimport { footer, isReady, mainView, masthead } from './views.ts';\n\nconst props = { path: prop.string({ required: true }) };\n\nexport interface AppState {\n readonly page: Page;\n /** A navigation happened and its content isn't on screen yet: focus the h1 when it is. */\n readonly focusPending: boolean;\n /** True once hydrated; exposed to CSS and tests as `jy-app:state(live)`. */\n readonly live: boolean;\n}\n\nexport type AppMsg = { readonly _tag: 'Routed'; readonly location: RouteLocation };\n\n/** Commands that make sure the catalog holds what `page` shows. */\nexport function loadFor(page: Page, c: CatalogState): Command<AppMsg>[] {\n if ((page._tag === 'Home' || page._tag === 'Category') && c.summaries === null)\n return [send(catalog, { _tag: 'LoadSummaries' })];\n if (page._tag === 'Game') {\n const commands: Command<AppMsg>[] = [];\n if (!(page.id in c.games)) commands.push(send(catalog, { _tag: 'LoadGame', id: page.id }));\n // The game page's \"More games\" list reads the summaries.\n if (c.summaries === null) commands.push(send(catalog, { _tag: 'LoadSummaries' }));\n return commands;\n }\n return [];\n}\n\nconst focusHeading = () => focus('main h1');\n\nexport const App = define<AppState, AppMsg, { readonly path: string }>()('jy-app', {\n shadow: false,\n props,\n stores: [catalog],\n init: (p) => ({ page: pageFor(p.path), focusPending: false, live: false }),\n subscriptions: () => [listen((location): AppMsg => ({ _tag: 'Routed', location }))],\n states: (s) => ({ live: s.live }),\n intent: {},\n update: {\n Routed: (s, m, { read }) => {\n const page = pageFor(m.location.pathname);\n if (samePage(page, s.page)) return s; // listen's first report: the URL the server rendered\n const c = read(catalog);\n const ready = isReady(page, c);\n return [\n { ...s, page, focusPending: !ready },\n [...loadFor(page, c), setHead(toGyralHead(headFor(page, c))), ...(ready ? [focusHeading()] : [])],\n ];\n },\n Hydrated: (s) => ({ ...s, live: true }),\n // Data for the current page arrived: refresh the head, and focus the h1 once it exists.\n StoreChanged: (s, m) => {\n const c = changed(catalog, m);\n if (c === undefined) return s;\n const ready = s.focusPending && isReady(s.page, c.state);\n return [\n { ...s, focusPending: s.focusPending && !ready },\n [setHead(toGyralHead(headFor(s.page, c.state))), ...(ready ? [focusHeading()] : [])],\n ];\n },\n },\n view: (s, _i, { read }) => {\n const c = read(catalog);\n return html`${masthead(s.page, c)}\n <main id=\"main\" tabindex=\"-1\">${mainView(s.page, c)}</main>\n ${footer()}`;\n },\n});\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'jy-app': InstanceType<typeof App>;\n }\n}\n"],"mappings":";;;;;;;;;AAAA,IAAA,YAAA;CAAA,MAAA;CAAA,QAAA;CAAA,SAAA;EAAA;GAAA;GAAA,CAAA;GAAA;EAAA;EAAA,CAAA,GAAA,CAAA,CAAA,CAAA;EAAA,CAAA,GAAA,CAAA,CAAA;CAAA;CAAA,YAAA;EAAA,EAAA,KAAA,QAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA,EAAA,KAAA,QAAA;CAAA;AAAA;AAcA,IAAA,QAAA,EAAA,MAAA,KAAA,OAAA,EAAA,UAAA,KAAA,CAAA,EAAA;;AAaA,SAAA,QAAA,MAAA,GAAA;CACA,KAAA,KAAA,SAAA,UAAA,KAAA,SAAA,eAAA,EAAA,cAAA,MACA,OAAA,CAAA,KAAA,SAAA,EAAA,MAAA,gBAAA,CAAA,CAAA;CACA,IAAA,KAAA,SAAA,QAAA;EACA,MAAA,WAAA,CAAA;EACA,IAAA,EAAA,KAAA,MAAA,EAAA,QAAA,SAAA,KAAA,KAAA,SAAA;GAAA,MAAA;GAAA,IAAA,KAAA;EAAA,CAAA,CAAA;EAEA,IAAA,EAAA,cAAA,MAAA,SAAA,KAAA,KAAA,SAAA,EAAA,MAAA,gBAAA,CAAA,CAAA;EACA,OAAA;CACA;CACA,OAAA,CAAA;AACA;AAEA,IAAA,qBAAA,MAAA,SAAA;AAEA,IAAA,MAAA,OAAA,CAAA,CAAA,UAAA;CACA,QAAA;CACA;CACA,QAAA,CAAA,OAAA;CACA,OAAA,OAAA;EAAA,MAAA,QAAA,EAAA,IAAA;EAAA,cAAA;EAAA,MAAA;CAAA;CACA,qBAAA,CAAA,QAAA,cAAA;EAAA,MAAA;EAAA;CAAA,EAAA,CAAA;CACA,SAAA,OAAA,EAAA,MAAA,EAAA,KAAA;CACA,QAAA,CAAA;CACA,QAAA;EACA,SAAA,GAAA,GAAA,EAAA,WAAA;GACA,MAAA,OAAA,QAAA,EAAA,SAAA,QAAA;GACA,IAAA,SAAA,MAAA,EAAA,IAAA,GAAA,OAAA;GACA,MAAA,IAAA,KAAA,OAAA;GACA,MAAA,QAAA,QAAA,MAAA,CAAA;GACA,OAAA,CACA;IAAA,GAAA;IAAA;IAAA,cAAA,CAAA;GAAA,GACA;IAAA,GAAA,QAAA,MAAA,CAAA;IAAA,QAAA,YAAA,QAAA,MAAA,CAAA,CAAA,CAAA;IAAA,GAAA,QAAA,CAAA,aAAA,CAAA,IAAA,CAAA;GAAA,CACA;EACA;EACA,WAAA,OAAA;GAAA,GAAA;GAAA,MAAA;EAAA;EAEA,eAAA,GAAA,MAAA;GACA,MAAA,IAAA,QAAA,SAAA,CAAA;GACA,IAAA,MAAA,KAAA,GAAA,OAAA;GACA,MAAA,QAAA,EAAA,gBAAA,QAAA,EAAA,MAAA,EAAA,KAAA;GACA,OAAA,CACA;IAAA,GAAA;IAAA,cAAA,EAAA,gBAAA,CAAA;GAAA,GACA,CAAA,QAAA,YAAA,QAAA,EAAA,MAAA,EAAA,KAAA,CAAA,CAAA,GAAA,GAAA,QAAA,CAAA,aAAA,CAAA,IAAA,CAAA,CAAA,CACA;EACA;CACA;CACA,OAAA,GAAA,IAAA,EAAA,WAAA;EACA,MAAA,IAAA,KAAA,OAAA;EACA,OAAWA,SAAA,WAAA;GAAO,SAAA,EAAA,MAAA,CAAA;GAAmB,SACC,EAAA,MAAA,CAAA;GAAmB,OACjD;EAAQ,CAAA;CAChB;AACA,CAAA"}
@@ -0,0 +1,2 @@
1
+ import type { Leaving } from '../store.js';
2
+ export declare const leavingView: (l: Leaving) => import("@gyral/core").TemplateResult;
@@ -0,0 +1,104 @@
1
+ import { SITE_NAME } from "../seo.js";
2
+ import { compiled } from "@gyral/core/compiled";
3
+ import "@gyral/core";
4
+ //#region src/components/leaving.ts
5
+ var _gyral$t0 = {
6
+ "id": "l5n7n2qrk3",
7
+ "html": "<section class=\"leaving\" aria-labelledby=\"leaving-heading\"><p class=\"kicker\">Advertisement</p><h1 id=\"leaving-heading\" tabindex=\"-1\">You’re leaving </h1><p class=\"lede\">The ad you chose goes to <strong class=\"leaving-domain\"></strong>, a website <!----> doesn’t run. Its own rules apply there.</p><ul class=\"leaving-links\" role=\"list\"><li><a class=\"cta\" rel=\"sponsored noopener\">Continue to </a></li><li><a class=\"back\">Back to </a></li></ul></section>",
8
+ "parts": [
9
+ [0, [0, 1]],
10
+ [1, [
11
+ 0,
12
+ 2,
13
+ 1
14
+ ]],
15
+ [
16
+ 2,
17
+ [0, 2],
18
+ 3
19
+ ],
20
+ [
21
+ 3,
22
+ [
23
+ 0,
24
+ 3,
25
+ 0,
26
+ 0
27
+ ],
28
+ "href"
29
+ ],
30
+ [0, [
31
+ 0,
32
+ 3,
33
+ 0,
34
+ 0
35
+ ]],
36
+ [
37
+ 3,
38
+ [
39
+ 0,
40
+ 3,
41
+ 1,
42
+ 0
43
+ ],
44
+ "href"
45
+ ],
46
+ [0, [
47
+ 0,
48
+ 3,
49
+ 1,
50
+ 0
51
+ ]]
52
+ ],
53
+ "segments": [
54
+ "<section class=\"leaving\" aria-labelledby=\"leaving-heading\"><p class=\"kicker\">Advertisement</p><h1 id=\"leaving-heading\" tabindex=\"-1\">You’re leaving ",
55
+ {
56
+ "k": "child",
57
+ "in": "h1"
58
+ },
59
+ "</h1><p class=\"lede\">The ad you chose goes to <strong class=\"leaving-domain\">",
60
+ {
61
+ "k": "child",
62
+ "in": "strong"
63
+ },
64
+ "</strong>, a website ",
65
+ {
66
+ "k": "child",
67
+ "in": "p"
68
+ },
69
+ "<!----> doesn’t run. Its own rules apply there.</p><ul class=\"leaving-links\" role=\"list\"><li><a class=\"cta\" rel=\"sponsored noopener\"",
70
+ {
71
+ "k": "attr",
72
+ "name": "href"
73
+ },
74
+ ">Continue to ",
75
+ {
76
+ "k": "child",
77
+ "in": "a"
78
+ },
79
+ "</a></li><li><a class=\"back\"",
80
+ {
81
+ "k": "attr",
82
+ "name": "href"
83
+ },
84
+ ">Back to ",
85
+ {
86
+ "k": "child",
87
+ "in": "a"
88
+ },
89
+ "</a></li></ul></section>"
90
+ ]
91
+ };
92
+ var leavingView = (l) => compiled(_gyral$t0, [
93
+ SITE_NAME,
94
+ l.domain,
95
+ SITE_NAME,
96
+ l.href,
97
+ l.domain,
98
+ l.back,
99
+ SITE_NAME
100
+ ]);
101
+ //#endregion
102
+ export { leavingView };
103
+
104
+ //# sourceMappingURL=leaving.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"leaving.js","names":["_gyral$compiled"],"sources":["../../src/components/leaving.ts"],"sourcesContent":["// \"You're leaving Joystyk\": the page an ad click opens before the advertiser's site\n// (advertising-policy.md, section 6; monetization.md, \"Counting\"). It names where the ad goes\n// and offers two plain links, so it works without JavaScript: on to the advertiser, straight\n// to the creative's approved link, or back to the page the visitor came from. The site's ad\n// routes count the click and seed the destination; nothing here reads the request.\nimport { html } from '@gyral/core';\nimport { SITE_NAME } from '../seo.ts';\nimport type { Leaving } from '../store.ts';\n\nexport const leavingView = (l: Leaving) => html`<section class=\"leaving\" aria-labelledby=\"leaving-heading\">\n <p class=\"kicker\">Advertisement</p>\n <h1 id=\"leaving-heading\" tabindex=\"-1\">You’re leaving ${SITE_NAME}</h1>\n <p class=\"lede\">\n The ad you chose goes to <strong class=\"leaving-domain\">${l.domain}</strong>, a website ${SITE_NAME} doesn’t run. Its\n own rules apply there.\n </p>\n <ul class=\"leaving-links\" role=\"list\">\n <li><a class=\"cta\" href=${l.href} rel=\"sponsored noopener\">Continue to ${l.domain}</a></li>\n <li><a class=\"back\" href=${l.back}>Back to ${SITE_NAME}</a></li>\n </ul>\n</section>`;\n"],"mappings":";;;;AAAA,IAAA,YAAA;CAAA,MAAA;CAAA,QAAA;CAAA,SAAA;EAAA,CAAA,GAAA,CAAA,GAAA,CAAA,CAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA;GAAA;GAAA,CAAA,GAAA,CAAA;GAAA;EAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;EAAA;GAAA;GAAA;IAAA;IAAA;IAAA;IAAA;GAAA;GAAA;EAAA;EAAA,CAAA,GAAA;GAAA;GAAA;GAAA;GAAA;EAAA,CAAA;CAAA;CAAA,YAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,QAAA;EAAA;EAAA;EAAA;GAAA,KAAA;GAAA,MAAA;EAAA;EAAA;CAAA;AAAA;AASA,IAAA,eAAA,MAA2CA,SAAA,WAAA;CAAA;CAEwB,EAEL;CAAgC;CAAS,EAIzE;CAA+C,EAAA;CAAQ,EACtD;CAAkB;AAAS,CAAA"}
@@ -0,0 +1,13 @@
1
+ import '@joystyk/launcher';
2
+ import type { LauncherGame } from '@joystyk/launcher';
3
+ import '@joystyk/ui/ad-slot';
4
+ import { type Page } from '../routes.js';
5
+ import type { CatalogState } from '../store.js';
6
+ import type { GameDetail } from '../wire.js';
7
+ /** What the launcher needs from a game page's data. */
8
+ export declare const launcherGameOf: (g: GameDetail) => LauncherGame;
9
+ /** True when the page's content (and so its h1) is on screen, rather than a loading line. */
10
+ export declare function isReady(page: Page, c: CatalogState): boolean;
11
+ export declare function mainView(page: Page, c: CatalogState): import("@gyral/core").TemplateResult;
12
+ export declare const masthead: (page: Page, c: CatalogState) => import("@gyral/core").TemplateResult;
13
+ export declare const footer: () => import("@gyral/core").TemplateResult;