create-cogenta 0.5.2 → 0.6.7
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.
- package/dist/blueprint-defaults.js +1 -1
- package/dist/blueprint-defaults.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/playground-reset.js +1 -1
- package/dist/playground-reset.js.map +1 -1
- package/dist/recap.js +1 -1
- package/dist/recap.js.map +1 -1
- package/dist/scaffold.d.ts +4 -2
- package/dist/scaffold.d.ts.map +1 -1
- package/dist/scaffold.js +10 -6
- package/dist/scaffold.js.map +1 -1
- package/dist/wizard.js +1 -1
- package/dist/wizard.js.map +1 -1
- package/package.json +14 -13
- package/dist/blueprints/assets/photos/association/community-cleanup.jpg +0 -0
- package/dist/blueprints/assets/photos/association/fundraising-dinner.jpg +0 -0
- package/dist/blueprints/assets/photos/association/garden-planting.jpg +0 -0
- package/dist/blueprints/assets/photos/association/harvest-food-drive.jpg +0 -0
- package/dist/blueprints/assets/photos/association/hero.jpg +0 -0
- package/dist/blueprints/assets/photos/association/volunteer-orientation.jpg +0 -0
- package/dist/blueprints/assets/photos/association/winter-coat-collection.jpg +0 -0
- package/dist/blueprints/assets/photos/blog/blog-runs-itself.jpg +0 -0
- package/dist/blueprints/assets/photos/blog/desk-setup.jpg +0 -0
- package/dist/blueprints/assets/photos/blog/editing.jpg +0 -0
- package/dist/blueprints/assets/photos/blog/hero.jpg +0 -0
- package/dist/blueprints/assets/photos/blog/notebooks.jpg +0 -0
- package/dist/blueprints/assets/photos/blog/plain-text-editor.jpg +0 -0
- package/dist/blueprints/assets/photos/blog/reading-nonfiction.jpg +0 -0
- package/dist/blueprints/assets/photos/blog/small-tool.jpg +0 -0
- package/dist/blueprints/assets/photos/blog/writing-schedule.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/business-2.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/business-3.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/business.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/culture-2.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/culture-3.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/culture.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/news-2.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/news-3.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/news.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/opinion-2.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/opinion-3.jpg +0 -0
- package/dist/blueprints/assets/photos/magazine/opinion.jpg +0 -0
- package/dist/blueprints/assets/photos/portfolio/adatum-publishing.jpg +0 -0
- package/dist/blueprints/assets/photos/portfolio/contoso-mobile-app.jpg +0 -0
- package/dist/blueprints/assets/photos/portfolio/fabrikam-annual-report.jpg +0 -0
- package/dist/blueprints/assets/photos/portfolio/hero.jpg +0 -0
- package/dist/blueprints/assets/photos/portfolio/litware-signage.jpg +0 -0
- package/dist/blueprints/assets/photos/portfolio/northwind-rebrand.jpg +0 -0
- package/dist/blueprints/assets/photos/portfolio/proseware-launch.jpg +0 -0
- package/dist/blueprints/assets/photos/portfolio/tailspin-streaming.jpg +0 -0
- package/dist/blueprints/assets/photos/portfolio/wingtip-wayfinding.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/charred-octopus.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/chocolate-tart.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/creme-brulee.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/hero.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/house-red.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/house-white.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/pan-seared-trout.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/poached-pear.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/roasted-beet-salad.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/slow-roast-duck-leg.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/soup-of-the-day.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/sparkling-water.jpg +0 -0
- package/dist/blueprints/assets/photos/restaurant/wild-mushroom-risotto.jpg +0 -0
- package/dist/blueprints/assets/photos/saas/avatar-1.jpg +0 -0
- package/dist/blueprints/assets/photos/saas/hero.jpg +0 -0
- package/dist/blueprints/assets/photos/store/camp-blanket.jpg +0 -0
- package/dist/blueprints/assets/photos/store/canvas-tote.jpg +0 -0
- package/dist/blueprints/assets/photos/store/cast-iron-skillet.jpg +0 -0
- package/dist/blueprints/assets/photos/store/ceramic-pour-over-set.jpg +0 -0
- package/dist/blueprints/assets/photos/store/enamel-mug.jpg +0 -0
- package/dist/blueprints/assets/photos/store/everyday-tee.jpg +0 -0
- package/dist/blueprints/assets/photos/store/field-jacket.jpg +0 -0
- package/dist/blueprints/assets/photos/store/hero.jpg +0 -0
- package/dist/blueprints/assets/photos/store/leather-card-holder.jpg +0 -0
- package/dist/blueprints/assets/photos/store/linen-table-runner.jpg +0 -0
- package/dist/blueprints/assets/photos/store/trail-tote.jpg +0 -0
- package/dist/blueprints/assets/photos/store/wool-beanie.jpg +0 -0
- package/dist/blueprints/assets/photos/store/wool-overshirt.jpg +0 -0
- package/dist/blueprints/assets/photos/vitrine/avatar-1.jpg +0 -0
- package/dist/blueprints/assets/photos/vitrine/avatar-2.jpg +0 -0
- package/dist/blueprints/assets/photos/vitrine/avatar-3.jpg +0 -0
- package/dist/blueprints/assets/photos/vitrine/dashboard.jpg +0 -0
- package/dist/blueprints/assets/photos/vitrine/hero.jpg +0 -0
- package/dist/blueprints/association.d.ts +0 -151
- package/dist/blueprints/association.d.ts.map +0 -1
- package/dist/blueprints/association.js +0 -732
- package/dist/blueprints/association.js.map +0 -1
- package/dist/blueprints/blog.d.ts +0 -198
- package/dist/blueprints/blog.d.ts.map +0 -1
- package/dist/blueprints/blog.js +0 -665
- package/dist/blueprints/blog.js.map +0 -1
- package/dist/blueprints/content-pack.d.ts +0 -143
- package/dist/blueprints/content-pack.d.ts.map +0 -1
- package/dist/blueprints/content-pack.js +0 -111
- package/dist/blueprints/content-pack.js.map +0 -1
- package/dist/blueprints/content-packs.d.ts +0 -11
- package/dist/blueprints/content-packs.d.ts.map +0 -1
- package/dist/blueprints/content-packs.js +0 -29
- package/dist/blueprints/content-packs.js.map +0 -1
- package/dist/blueprints/demo-media.d.ts +0 -39
- package/dist/blueprints/demo-media.d.ts.map +0 -1
- package/dist/blueprints/demo-media.js +0 -30
- package/dist/blueprints/demo-media.js.map +0 -1
- package/dist/blueprints/documentation.d.ts +0 -134
- package/dist/blueprints/documentation.d.ts.map +0 -1
- package/dist/blueprints/documentation.js +0 -678
- package/dist/blueprints/documentation.js.map +0 -1
- package/dist/blueprints/magazine.d.ts +0 -145
- package/dist/blueprints/magazine.d.ts.map +0 -1
- package/dist/blueprints/magazine.js +0 -543
- package/dist/blueprints/magazine.js.map +0 -1
- package/dist/blueprints/menus.d.ts +0 -25
- package/dist/blueprints/menus.d.ts.map +0 -1
- package/dist/blueprints/menus.js +0 -33
- package/dist/blueprints/menus.js.map +0 -1
- package/dist/blueprints/photo-assets.d.ts +0 -3
- package/dist/blueprints/photo-assets.d.ts.map +0 -1
- package/dist/blueprints/photo-assets.js +0 -33
- package/dist/blueprints/photo-assets.js.map +0 -1
- package/dist/blueprints/portfolio.d.ts +0 -141
- package/dist/blueprints/portfolio.d.ts.map +0 -1
- package/dist/blueprints/portfolio.js +0 -700
- package/dist/blueprints/portfolio.js.map +0 -1
- package/dist/blueprints/restaurant.d.ts +0 -124
- package/dist/blueprints/restaurant.d.ts.map +0 -1
- package/dist/blueprints/restaurant.js +0 -457
- package/dist/blueprints/restaurant.js.map +0 -1
- package/dist/blueprints/saas.d.ts +0 -133
- package/dist/blueprints/saas.d.ts.map +0 -1
- package/dist/blueprints/saas.js +0 -551
- package/dist/blueprints/saas.js.map +0 -1
- package/dist/blueprints/site-settings-seed.d.ts +0 -17
- package/dist/blueprints/site-settings-seed.d.ts.map +0 -1
- package/dist/blueprints/site-settings-seed.js +0 -45
- package/dist/blueprints/site-settings-seed.js.map +0 -1
- package/dist/blueprints/starting-skins.d.ts +0 -19
- package/dist/blueprints/starting-skins.d.ts.map +0 -1
- package/dist/blueprints/starting-skins.js +0 -301
- package/dist/blueprints/starting-skins.js.map +0 -1
- package/dist/blueprints/store.d.ts +0 -173
- package/dist/blueprints/store.d.ts.map +0 -1
- package/dist/blueprints/store.js +0 -643
- package/dist/blueprints/store.js.map +0 -1
- package/dist/blueprints/vitrine.d.ts +0 -178
- package/dist/blueprints/vitrine.d.ts.map +0 -1
- package/dist/blueprints/vitrine.js +0 -529
- package/dist/blueprints/vitrine.js.map +0 -1
- package/dist/demo-art/compositions.d.ts +0 -48
- package/dist/demo-art/compositions.d.ts.map +0 -1
- package/dist/demo-art/compositions.js +0 -690
- package/dist/demo-art/compositions.js.map +0 -1
- package/dist/demo-art/index.d.ts +0 -8
- package/dist/demo-art/index.d.ts.map +0 -1
- package/dist/demo-art/index.js +0 -5
- package/dist/demo-art/index.js.map +0 -1
- package/dist/demo-art/oklch.d.ts +0 -39
- package/dist/demo-art/oklch.d.ts.map +0 -1
- package/dist/demo-art/oklch.js +0 -79
- package/dist/demo-art/oklch.js.map +0 -1
- package/dist/demo-art/png.d.ts +0 -24
- package/dist/demo-art/png.d.ts.map +0 -1
- package/dist/demo-art/png.js +0 -110
- package/dist/demo-art/png.js.map +0 -1
- package/dist/demo-art/render.d.ts +0 -228
- package/dist/demo-art/render.d.ts.map +0 -1
- package/dist/demo-art/render.js +0 -478
- package/dist/demo-art/render.js.map +0 -1
|
@@ -1,700 +0,0 @@
|
|
|
1
|
-
import { CogentaError } from '@cogenta/core';
|
|
2
|
-
import { createContentStore, defineCollection, f, validateCollectionSet, } from '@cogenta/schema';
|
|
3
|
-
import { avatarArt, coverArt, heroArt, logoArt } from '../demo-art/compositions.js';
|
|
4
|
-
import { definePageCollection, richTextParagraph, SEO_FIELDS, toBlockZoneEntry, } from './content-pack.js';
|
|
5
|
-
import { STARTING_SKINS } from './starting-skins.js';
|
|
6
|
-
/**
|
|
7
|
-
* The `portfolio` blueprint (L25 Annexe brief, `theme-portfolio` pro pass):
|
|
8
|
-
* a `project` collection — cover image, `role`/`year` as plain structured
|
|
9
|
-
* fields (a `collectionList` grid card and a project's own page both read
|
|
10
|
-
* them straight off the entry, `@cogenta/theme-portfolio`'s own doing, not
|
|
11
|
-
* this blueprint's) — plus a genuinely rich single-page home the theme
|
|
12
|
-
* renders as full-bleed project cards, a services grid with real icons, a
|
|
13
|
-
* client logo strip and a two-part client voice (`quote` then
|
|
14
|
-
* `testimonial`).
|
|
15
|
-
*
|
|
16
|
-
* Rewritten in full for the L25 pro pass (its L9/L23 predecessor seeded
|
|
17
|
-
* three home blocks, three projects, no visuals and no menus at all):
|
|
18
|
-
* eight projects with distinct cover compositions, a nine-block home, an
|
|
19
|
-
* about page, a contact page, a legal page, real header/footer/header-
|
|
20
|
-
* action navigation, and `@cogenta/theme-portfolio` as the default theme.
|
|
21
|
-
*/
|
|
22
|
-
export const project = defineCollection({
|
|
23
|
-
name: 'project',
|
|
24
|
-
labels: { singular: 'Project', plural: 'Projects' },
|
|
25
|
-
routing: { pattern: '/work/:slug' },
|
|
26
|
-
fields: {
|
|
27
|
-
title: f.text({ required: true, max: 200 }),
|
|
28
|
-
slug: f.slug({ from: 'title', unique: true }),
|
|
29
|
-
summary: f.text({ max: 300, multiline: true }),
|
|
30
|
-
role: f.text({ max: 120 }),
|
|
31
|
-
year: f.text({ max: 4 }),
|
|
32
|
-
coverImage: f.media({ accept: ['image'] }),
|
|
33
|
-
// Optional body a project page renders after its own header — the
|
|
34
|
-
// seed below places one auto-built "Role / Year" panel here (a flat,
|
|
35
|
-
// muted-background `prose` block, `blocks@2.0`'s `variant`), the
|
|
36
|
-
// in-scope way this theme shows those two raw fields on the project's
|
|
37
|
-
// own page (`renderEntryHeader` draws only title/cover/excerpt from
|
|
38
|
-
// contract D `theme@1.4`'s fixed `PageEntryMeta` shape, which has no
|
|
39
|
-
// room for a collection's own custom fields) — never required, so a
|
|
40
|
-
// project an editor creates by hand without one still renders fine.
|
|
41
|
-
blocks: f.blocks(),
|
|
42
|
-
...SEO_FIELDS,
|
|
43
|
-
},
|
|
44
|
-
indexes: [['slug']],
|
|
45
|
-
permissions: {
|
|
46
|
-
read: ['public'],
|
|
47
|
-
create: ['editor', 'admin'],
|
|
48
|
-
update: ['editor', 'admin'],
|
|
49
|
-
delete: ['admin'],
|
|
50
|
-
publish: ['admin'],
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
export const page = definePageCollection('/:slug');
|
|
54
|
-
export const PORTFOLIO_COLLECTIONS = [project, page];
|
|
55
|
-
validateCollectionSet(PORTFOLIO_COLLECTIONS);
|
|
56
|
-
export const PORTFOLIO_DEMO_PROJECTS = [
|
|
57
|
-
{
|
|
58
|
-
title: 'Northwind rebrand',
|
|
59
|
-
slug: 'northwind-rebrand',
|
|
60
|
-
summary: 'A full identity refresh for a 41-store regional grocery chain built by acquisition — one logotype and one packaging system across 340 private-label SKUs, rolled out without closing a single store.',
|
|
61
|
-
role: 'Art direction',
|
|
62
|
-
year: '2025',
|
|
63
|
-
coverSeed: 12,
|
|
64
|
-
narrative: [
|
|
65
|
-
'Northwind runs forty-one stores across the Pacific Northwest, most of them acquired rather than built — which meant four legacy logotypes, three shades of the same green, and a private-label range that looked like six different companies. The brief was blunt: make it read as one grocer without asking anyone to repaint a storefront this year.',
|
|
66
|
-
'We shipped a logotype family with two registers (a plain wordmark for the storefront, a warmer hand for seasonal signage), a 68-page brand guideline, and new packaging for 340 private-label SKUs, phased across three print runs so nothing hit the shelf half-rebranded. Every store kept trading through every stage — no reprint weekend, no relaunch, no closure.',
|
|
67
|
-
],
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
title: 'Contoso mobile app',
|
|
71
|
-
slug: 'contoso-mobile-app',
|
|
72
|
-
summary: 'A ground-up component library and interaction redesign for a fintech app used by two million people, rebuilt around one confirmation pattern for every place money moves.',
|
|
73
|
-
role: 'Product design',
|
|
74
|
-
year: '2025',
|
|
75
|
-
coverSeed: 27,
|
|
76
|
-
narrative: [
|
|
77
|
-
"Contoso's banking app had grown a screen at a time for six years, and it showed: four different date pickers, three ways to confirm a transfer, and a design team that had never run a full component audit. Two million people used it anyway, mostly out of habit.",
|
|
78
|
-
'We rebuilt the library from the transaction list outward — forty-two components, one motion language, one confirmation pattern used everywhere money changes hands — and shipped it behind a flag to five percent of accounts before the full rollout. Support tickets about "where did my transfer go" dropped by a third in the first month.',
|
|
79
|
-
],
|
|
80
|
-
},
|
|
81
|
-
{
|
|
82
|
-
title: 'Fabrikam annual report',
|
|
83
|
-
slug: 'fabrikam-annual-report',
|
|
84
|
-
summary: 'Editorial design and typesetting for a 60-page annual report — print and an interactive web edition from the same grid, at a 4,000-copy print run.',
|
|
85
|
-
role: 'Editorial design',
|
|
86
|
-
year: '2024',
|
|
87
|
-
coverSeed: 41,
|
|
88
|
-
narrative: [
|
|
89
|
-
"A 60-page annual report is read closely by maybe a hundred people and skimmed by ten thousand more, so it has to work as a spreadsheet and as a piece of writing at the same time. Fabrikam's finance team wrote the copy; we built the grid that lets a dense results table share a page with a proper photo essay without either one losing.",
|
|
90
|
-
"Print run of 4,000, plus an interactive web edition built from the same InDesign grid so the numbers stay legible on a phone. The typesetting keeps a single serif for every numeral in the book — auditors notice when a table changes font mid-report, and we'd rather they didn't have the chance to.",
|
|
91
|
-
],
|
|
92
|
-
},
|
|
93
|
-
{
|
|
94
|
-
title: 'Litware signage system',
|
|
95
|
-
slug: 'litware-signage-system',
|
|
96
|
-
summary: 'Wayfinding and environmental graphics for a 12-storey, four-wing campus — 214 signs and a lobby directory that updates from a spreadsheet, not a repaint.',
|
|
97
|
-
role: 'Signage & environmental',
|
|
98
|
-
year: '2024',
|
|
99
|
-
coverSeed: 58,
|
|
100
|
-
narrative: [
|
|
101
|
-
"Twelve storeys, four wings, one loading dock nobody could find without asking twice. Litware's campus had been designed by three different architecture firms over eight years, and the wayfinding showed it — or rather, the near-total absence of it.",
|
|
102
|
-
'We proofed every sign on the actual material before a single one went into production: brushed aluminium in daylight reads nothing like the same file on a monitor. 214 signs, one pictogram set, and a lobby directory that updates from a single spreadsheet rather than a repaint every time a department changes floors.',
|
|
103
|
-
],
|
|
104
|
-
},
|
|
105
|
-
{
|
|
106
|
-
title: 'Tailspin streaming identity',
|
|
107
|
-
slug: 'tailspin-streaming-identity',
|
|
108
|
-
summary: 'A motion-first brand system for a live-sports streaming launch across five markets — one grid, five market variants, built to survive a jumbotron and a phone screen alike.',
|
|
109
|
-
role: 'Brand & motion',
|
|
110
|
-
year: '2023',
|
|
111
|
-
coverSeed: 63,
|
|
112
|
-
narrative: [
|
|
113
|
-
"Tailspin launched live sports streaming in five markets on the same night, which meant the identity had to survive a stadium jumbotron, a phone held sideways and a broadcast truck's colour grading — often all three during the same match.",
|
|
114
|
-
'The system is built motion-first: the mark only fully resolves once it moves, so every static application — kit, ticket, merchandise — is a single frame pulled from the same animation rather than a logo redrawn to match. Five market-specific colour variants share one grid, and the sixth market that signed on two months after launch needed zero redraws to join it.',
|
|
115
|
-
],
|
|
116
|
-
},
|
|
117
|
-
{
|
|
118
|
-
title: 'Adatum publishing house',
|
|
119
|
-
slug: 'adatum-publishing-house',
|
|
120
|
-
summary: 'Cover systems and typesetting for a twelve-title fiction imprint, with one fixed typographic rule and imagery that shifts by genre.',
|
|
121
|
-
role: 'Editorial design',
|
|
122
|
-
year: '2023',
|
|
123
|
-
coverSeed: 79,
|
|
124
|
-
narrative: [
|
|
125
|
-
'Twelve titles under one imprint, and a house style that had to hold across a literary novel, a thriller and a story collection without any of the three looking like they belonged on the wrong shelf.',
|
|
126
|
-
'We built a cover system with one fixed typographic rule — the title always sets in the same face, always in the same position on the spine — and let the imagery vary completely by genre: commissioned illustration for the literary list, photography for the thrillers. Booksellers could tell the imprint from three metres away by the second season.',
|
|
127
|
-
],
|
|
128
|
-
},
|
|
129
|
-
{
|
|
130
|
-
title: 'Wingtip terminal wayfinding',
|
|
131
|
-
slug: 'wingtip-terminal-wayfinding',
|
|
132
|
-
summary: 'Signage, pictograms and a colour-coded wayfinding system for a regional airport terminal, tested against real foot traffic before fabrication.',
|
|
133
|
-
role: 'Signage & environmental',
|
|
134
|
-
year: '2022',
|
|
135
|
-
coverSeed: 88,
|
|
136
|
-
narrative: [
|
|
137
|
-
"A regional airport's wayfinding has about four seconds to work before a traveller who is already late stops trusting it and starts asking a stranger. Wingtip's terminal had grown three extensions since 1994, and its signage had grown with it — six typefaces, and two colour codes that meant different things on different concourses.",
|
|
138
|
-
'The new system reduces the terminal to three colour-coded zones and one pictogram family, tested against real foot traffic during an actual boarding rush before a single panel was fabricated. Gate-finding complaints at the information desk fell by half in the first reporting quarter.',
|
|
139
|
-
],
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
title: 'Proseware product launch',
|
|
143
|
-
slug: 'proseware-product-launch',
|
|
144
|
-
summary: 'Packaging, retail displays and a launch film for a home-audio product line, shot and shipped in eleven weeks to hit 900 stores on schedule.',
|
|
145
|
-
role: 'Art direction',
|
|
146
|
-
year: '2022',
|
|
147
|
-
coverSeed: 96,
|
|
148
|
-
narrative: [
|
|
149
|
-
"Proseware's new speaker line needed to launch retail-ready in eleven weeks — packaging, in-store display and a two-minute film, all built around a product that was still changing in engineering the week we started shooting.",
|
|
150
|
-
'We designed the packaging to double as its own retail display, so no separate cardboard unit had to be fabricated and shipped, then shot the film against the same modular set the display uses. Assets were delivered in time for the line to hit 900 stores on the date the client had already announced. Nothing slipped.',
|
|
151
|
-
],
|
|
152
|
-
},
|
|
153
|
-
];
|
|
154
|
-
const BLOCK_VERSION = '1.0.0';
|
|
155
|
-
/**
|
|
156
|
-
* A short, structural rich-text list — no HTML in a block (R3), a bullet
|
|
157
|
-
* list is the honest equivalent of a two-row "spec sheet".
|
|
158
|
-
*/
|
|
159
|
-
function labelValueRow(key, label, value) {
|
|
160
|
-
return [
|
|
161
|
-
{
|
|
162
|
-
_key: `${key}-p`,
|
|
163
|
-
_type: 'block',
|
|
164
|
-
style: 'normal',
|
|
165
|
-
children: [
|
|
166
|
-
{ _key: `${key}-label`, _type: 'span', text: `${label} `, marks: ['strong'] },
|
|
167
|
-
{ _key: `${key}-value`, _type: 'span', text: value, marks: [] },
|
|
168
|
-
],
|
|
169
|
-
markDefs: [],
|
|
170
|
-
},
|
|
171
|
-
];
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* The auto-built "Role / Year" panel a project's own page shows beneath
|
|
175
|
-
* `renderEntryHeader` — a flat, muted-background `prose` block
|
|
176
|
-
* (`blocks@2.0`'s `variant.background`, an existing envelope field every
|
|
177
|
-
* block already carries, not a new one) built from the same `role`/`year`
|
|
178
|
-
* text the project's own fields hold. `@cogenta/theme-portfolio`'s
|
|
179
|
-
* `[data-block][data-variant-background="muted"]` rule is what turns this
|
|
180
|
-
* into a flat panel — no block-specific CSS needed for it.
|
|
181
|
-
*/
|
|
182
|
-
function roleYearPanelBlock(project) {
|
|
183
|
-
const body = [
|
|
184
|
-
...labelValueRow(`${project.slug}-role`, 'Role', project.role),
|
|
185
|
-
...labelValueRow(`${project.slug}-year`, 'Year', project.year),
|
|
186
|
-
];
|
|
187
|
-
return {
|
|
188
|
-
_key: `${project.slug}-meta`,
|
|
189
|
-
_type: 'prose',
|
|
190
|
-
_version: BLOCK_VERSION,
|
|
191
|
-
body,
|
|
192
|
-
variant: { background: 'muted' },
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* The project's own narrative (L26 D1) — two real paragraphs of process and
|
|
197
|
-
* outcome, not the one-line `summary` repeated. Placed after
|
|
198
|
-
* {@link roleYearPanelBlock} on the project's own page.
|
|
199
|
-
*/
|
|
200
|
-
function projectNarrativeBlock(project) {
|
|
201
|
-
const body = project.narrative.flatMap((paragraph, index) => richTextParagraph(`${project.slug}-narrative-${index}`, paragraph));
|
|
202
|
-
return {
|
|
203
|
-
_key: `${project.slug}-narrative`,
|
|
204
|
-
_type: 'prose',
|
|
205
|
-
_version: BLOCK_VERSION,
|
|
206
|
-
body,
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* The home page's nine blocks (L25 Annexe brief): hero, "Selected work" (a
|
|
211
|
-
* full-bleed project grid), the numbers, services, clients, a quote, the
|
|
212
|
-
* full project index, a testimonial and the closing call to action. Every
|
|
213
|
-
* block's `_key` doubles as a real in-page anchor
|
|
214
|
-
* (`@cogenta/theme-portfolio`'s `renderPage`, which stamps a real `id` from
|
|
215
|
-
* it) — the header/footer menus below link straight into "Selected work"
|
|
216
|
-
* and "Services" rather than a dead `#fragment`.
|
|
217
|
-
*
|
|
218
|
-
* A function of `media` (`SeedContext.media`), not a static const: the
|
|
219
|
-
* hero/logo media fields need ids `seedDemoMedia` only knows at scaffold
|
|
220
|
-
* time. `buildPortfolioHomeBlocks({})` (no media) still returns a complete,
|
|
221
|
-
* valid block list — `hero`/`logoStrip` simply drop their media-bearing
|
|
222
|
-
* fields rather than emit an invalid block.
|
|
223
|
-
*/
|
|
224
|
-
export function buildPortfolioHomeBlocks(media) {
|
|
225
|
-
const logoItems = [1, 2, 3, 4, 5]
|
|
226
|
-
.map((n) => media[`logo-${n}`])
|
|
227
|
-
.filter((id) => id !== undefined)
|
|
228
|
-
.map((id, index) => ({ _key: `home-clients-${index}`, media: id }));
|
|
229
|
-
return [
|
|
230
|
-
{
|
|
231
|
-
_key: 'home-hero',
|
|
232
|
-
_type: 'hero',
|
|
233
|
-
_version: BLOCK_VERSION,
|
|
234
|
-
eyebrow: 'Independent studio, est. 2017',
|
|
235
|
-
title: 'A studio that ships in the open',
|
|
236
|
-
subtitle: 'Brand systems, product design and editorial work for teams who want to see it made, not just delivered.',
|
|
237
|
-
...(media.hero === undefined ? {} : { media: media.hero }),
|
|
238
|
-
actions: [
|
|
239
|
-
{ label: 'Selected work', target: { href: '/#home-work' }, emphasis: 'primary' },
|
|
240
|
-
{ label: "Let's talk", target: { href: '/contact' } },
|
|
241
|
-
],
|
|
242
|
-
},
|
|
243
|
-
{
|
|
244
|
-
_key: 'home-work',
|
|
245
|
-
_type: 'collectionList',
|
|
246
|
-
_version: BLOCK_VERSION,
|
|
247
|
-
title: 'Selected work',
|
|
248
|
-
collection: 'project',
|
|
249
|
-
sort: { field: 'createdAt', direction: 'desc' },
|
|
250
|
-
limit: 6,
|
|
251
|
-
layout: 'grid',
|
|
252
|
-
},
|
|
253
|
-
{
|
|
254
|
-
_key: 'home-stats',
|
|
255
|
-
_type: 'stats',
|
|
256
|
-
_version: BLOCK_VERSION,
|
|
257
|
-
title: 'At a glance',
|
|
258
|
-
items: [
|
|
259
|
-
{ _key: 'home-stats-1', value: '8', unit: 'years', label: 'in practice' },
|
|
260
|
-
{ _key: 'home-stats-2', value: '40+', label: 'projects delivered' },
|
|
261
|
-
{ _key: 'home-stats-3', value: '12', label: 'awards & mentions' },
|
|
262
|
-
],
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
_key: 'home-services',
|
|
266
|
-
_type: 'featureGrid',
|
|
267
|
-
_version: BLOCK_VERSION,
|
|
268
|
-
title: 'Services',
|
|
269
|
-
items: [
|
|
270
|
-
{
|
|
271
|
-
_key: 'home-service-1',
|
|
272
|
-
icon: 'pen',
|
|
273
|
-
title: 'Identity',
|
|
274
|
-
text: 'Naming, marks and the small rules that keep a brand recognisable once other people apply it.',
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
_key: 'home-service-2',
|
|
278
|
-
icon: 'book',
|
|
279
|
-
title: 'Editorial design',
|
|
280
|
-
text: 'Books, reports and long-form sites. Typography first, because that is where the reading happens.',
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
_key: 'home-service-3',
|
|
284
|
-
icon: 'layers',
|
|
285
|
-
title: 'Signage & environmental',
|
|
286
|
-
text: 'Things that get made once and have to be right. Proofed on the material, not on a screen.',
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
|
-
},
|
|
290
|
-
...(logoItems.length === 0
|
|
291
|
-
? []
|
|
292
|
-
: [
|
|
293
|
-
{
|
|
294
|
-
_key: 'home-clients',
|
|
295
|
-
_type: 'logoStrip',
|
|
296
|
-
_version: BLOCK_VERSION,
|
|
297
|
-
logos: logoItems,
|
|
298
|
-
caption: 'Selected clients',
|
|
299
|
-
},
|
|
300
|
-
]),
|
|
301
|
-
{
|
|
302
|
-
_key: 'home-quote',
|
|
303
|
-
_type: 'quote',
|
|
304
|
-
_version: BLOCK_VERSION,
|
|
305
|
-
text: 'They shipped a design system and a working site in the same sprint — and explained every decision along the way.',
|
|
306
|
-
author: 'A. Reviewer',
|
|
307
|
-
role: 'Head of brand, Northwind',
|
|
308
|
-
...(media['quote-avatar'] === undefined ? {} : { avatar: media['quote-avatar'] }),
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
_key: 'home-index',
|
|
312
|
-
_type: 'collectionList',
|
|
313
|
-
_version: BLOCK_VERSION,
|
|
314
|
-
title: 'The full index',
|
|
315
|
-
collection: 'project',
|
|
316
|
-
sort: { field: 'createdAt', direction: 'asc' },
|
|
317
|
-
limit: 8,
|
|
318
|
-
layout: 'list',
|
|
319
|
-
},
|
|
320
|
-
{
|
|
321
|
-
_key: 'home-testimonial',
|
|
322
|
-
_type: 'testimonial',
|
|
323
|
-
_version: BLOCK_VERSION,
|
|
324
|
-
quote: richTextParagraph('home-testimonial-quote', 'A quietly confident studio — the kind of considered work we keep coming back for.'),
|
|
325
|
-
attribution: {
|
|
326
|
-
name: 'M. Bernard',
|
|
327
|
-
role: 'Founder, Adatum',
|
|
328
|
-
...(media['testimonial-avatar'] === undefined
|
|
329
|
-
? {}
|
|
330
|
-
: { avatar: media['testimonial-avatar'] }),
|
|
331
|
-
},
|
|
332
|
-
},
|
|
333
|
-
{
|
|
334
|
-
_key: 'home-cta',
|
|
335
|
-
_type: 'cta',
|
|
336
|
-
_version: BLOCK_VERSION,
|
|
337
|
-
title: "Let's talk about your project",
|
|
338
|
-
text: 'One call, no obligation.',
|
|
339
|
-
actions: [{ label: 'Get in touch', target: { href: '/contact' }, emphasis: 'primary' }],
|
|
340
|
-
},
|
|
341
|
-
];
|
|
342
|
-
}
|
|
343
|
-
export function buildPortfolioDemoPages(media) {
|
|
344
|
-
return [
|
|
345
|
-
{ title: 'Home', slug: 'home', blocks: buildPortfolioHomeBlocks(media) },
|
|
346
|
-
{
|
|
347
|
-
title: 'About',
|
|
348
|
-
slug: 'about',
|
|
349
|
-
blocks: [
|
|
350
|
-
{
|
|
351
|
-
_key: 'about-prose',
|
|
352
|
-
_type: 'prose',
|
|
353
|
-
_version: BLOCK_VERSION,
|
|
354
|
-
body: [
|
|
355
|
-
{
|
|
356
|
-
_key: 'about-p1',
|
|
357
|
-
_type: 'block',
|
|
358
|
-
style: 'normal',
|
|
359
|
-
children: [
|
|
360
|
-
{
|
|
361
|
-
_key: 'about-p1-span',
|
|
362
|
-
_type: 'span',
|
|
363
|
-
text: 'We are a small studio working across identity, product and editorial design — usually all three on the same project, since a brand rarely stays inside one discipline for long. Founded in 2017, the team has stayed under ten people on purpose: the person who takes the brief is the person who ships the work, on every project, every time.',
|
|
364
|
-
marks: [],
|
|
365
|
-
},
|
|
366
|
-
],
|
|
367
|
-
markDefs: [],
|
|
368
|
-
},
|
|
369
|
-
{
|
|
370
|
-
_key: 'about-p2',
|
|
371
|
-
_type: 'block',
|
|
372
|
-
style: 'normal',
|
|
373
|
-
children: [
|
|
374
|
-
{
|
|
375
|
-
_key: 'about-p2-span',
|
|
376
|
-
_type: 'span',
|
|
377
|
-
text: "Most of what we do starts with an audit, not a mood board — cataloguing what a client already has, good and bad, before proposing anything new. It is slower at the start and faster everywhere after, because nobody discovers six months in that the new identity does not fit the loading dock, the packaging line, or an app's existing component library.",
|
|
378
|
-
marks: [],
|
|
379
|
-
},
|
|
380
|
-
],
|
|
381
|
-
markDefs: [],
|
|
382
|
-
},
|
|
383
|
-
{
|
|
384
|
-
_key: 'about-p3',
|
|
385
|
-
_type: 'block',
|
|
386
|
-
style: 'normal',
|
|
387
|
-
children: [
|
|
388
|
-
{
|
|
389
|
-
_key: 'about-p3-span',
|
|
390
|
-
_type: 'span',
|
|
391
|
-
text: 'We work from a studio in Lisbon with clients across Europe and North America, in the open where the brief allows it — process notes, working files and the odd dead end, published as we go rather than saved for a polished case study months later.',
|
|
392
|
-
marks: [],
|
|
393
|
-
},
|
|
394
|
-
],
|
|
395
|
-
markDefs: [],
|
|
396
|
-
},
|
|
397
|
-
],
|
|
398
|
-
},
|
|
399
|
-
...(media['about-figure'] === undefined
|
|
400
|
-
? []
|
|
401
|
-
: [
|
|
402
|
-
{
|
|
403
|
-
_key: 'about-figure',
|
|
404
|
-
_type: 'mediaFigure',
|
|
405
|
-
_version: BLOCK_VERSION,
|
|
406
|
-
media: media['about-figure'],
|
|
407
|
-
caption: 'The studio floor, mid-review.',
|
|
408
|
-
ratio: '16:9',
|
|
409
|
-
align: 'center',
|
|
410
|
-
},
|
|
411
|
-
]),
|
|
412
|
-
{
|
|
413
|
-
_key: 'about-stats',
|
|
414
|
-
_type: 'stats',
|
|
415
|
-
_version: BLOCK_VERSION,
|
|
416
|
-
title: 'At a glance',
|
|
417
|
-
items: [
|
|
418
|
-
{ _key: 'about-stat-1', value: '8', unit: 'years', label: 'in practice' },
|
|
419
|
-
{ _key: 'about-stat-2', value: '40+', label: 'projects delivered' },
|
|
420
|
-
{ _key: 'about-stat-3', value: '12', label: 'ongoing clients' },
|
|
421
|
-
],
|
|
422
|
-
},
|
|
423
|
-
],
|
|
424
|
-
},
|
|
425
|
-
{
|
|
426
|
-
title: 'Contact',
|
|
427
|
-
slug: 'contact',
|
|
428
|
-
blocks: [
|
|
429
|
-
{
|
|
430
|
-
_key: 'contact-prose',
|
|
431
|
-
_type: 'prose',
|
|
432
|
-
_version: BLOCK_VERSION,
|
|
433
|
-
body: [
|
|
434
|
-
{
|
|
435
|
-
_key: 'contact-p1',
|
|
436
|
-
_type: 'block',
|
|
437
|
-
style: 'normal',
|
|
438
|
-
children: [
|
|
439
|
-
{
|
|
440
|
-
_key: 'contact-p1-span',
|
|
441
|
-
_type: 'span',
|
|
442
|
-
text: 'Tell us about the project — timeline, budget range and what "done" looks like. We reply within two working days, always with a real person, never a form response.',
|
|
443
|
-
marks: [],
|
|
444
|
-
},
|
|
445
|
-
],
|
|
446
|
-
markDefs: [],
|
|
447
|
-
},
|
|
448
|
-
{
|
|
449
|
-
_key: 'contact-p2',
|
|
450
|
-
_type: 'block',
|
|
451
|
-
style: 'normal',
|
|
452
|
-
children: [
|
|
453
|
-
{
|
|
454
|
-
_key: 'contact-p2-span',
|
|
455
|
-
_type: 'span',
|
|
456
|
-
text: 'If it looks like a fit, the next step is a short call — thirty minutes, no deck — followed by a written scope within a week. We start most projects with the audit described on the About page, so the first invoice is usually smaller than people expect.',
|
|
457
|
-
marks: [],
|
|
458
|
-
},
|
|
459
|
-
],
|
|
460
|
-
markDefs: [],
|
|
461
|
-
},
|
|
462
|
-
],
|
|
463
|
-
},
|
|
464
|
-
{
|
|
465
|
-
_key: 'contact-features',
|
|
466
|
-
_type: 'featureGrid',
|
|
467
|
-
_version: BLOCK_VERSION,
|
|
468
|
-
title: 'What to expect',
|
|
469
|
-
items: [
|
|
470
|
-
{
|
|
471
|
-
_key: 'contact-feature-1',
|
|
472
|
-
icon: 'pen',
|
|
473
|
-
title: 'A real reply',
|
|
474
|
-
text: 'Two working days, from someone who will actually work on the project — not a scheduling bot.',
|
|
475
|
-
},
|
|
476
|
-
{
|
|
477
|
-
_key: 'contact-feature-2',
|
|
478
|
-
icon: 'layers',
|
|
479
|
-
title: 'A written scope',
|
|
480
|
-
text: 'Timeline, deliverables and price in writing before anything is billed.',
|
|
481
|
-
},
|
|
482
|
-
{
|
|
483
|
-
_key: 'contact-feature-3',
|
|
484
|
-
icon: 'book',
|
|
485
|
-
title: 'One point of contact',
|
|
486
|
-
text: 'The person who takes the brief stays on the project through delivery.',
|
|
487
|
-
},
|
|
488
|
-
],
|
|
489
|
-
},
|
|
490
|
-
{
|
|
491
|
-
_key: 'contact-cta',
|
|
492
|
-
_type: 'cta',
|
|
493
|
-
_version: BLOCK_VERSION,
|
|
494
|
-
title: "Let's talk",
|
|
495
|
-
text: 'One call, no obligation.',
|
|
496
|
-
actions: [
|
|
497
|
-
{
|
|
498
|
-
label: 'Email the studio',
|
|
499
|
-
target: { href: 'mailto:hello@studio.example' },
|
|
500
|
-
emphasis: 'primary',
|
|
501
|
-
},
|
|
502
|
-
],
|
|
503
|
-
},
|
|
504
|
-
],
|
|
505
|
-
},
|
|
506
|
-
{
|
|
507
|
-
title: 'Legal',
|
|
508
|
-
slug: 'legal',
|
|
509
|
-
blocks: [
|
|
510
|
-
{
|
|
511
|
-
_key: 'legal-prose',
|
|
512
|
-
_type: 'prose',
|
|
513
|
-
_version: BLOCK_VERSION,
|
|
514
|
-
body: [
|
|
515
|
-
{
|
|
516
|
-
_key: 'legal-p1',
|
|
517
|
-
_type: 'block',
|
|
518
|
-
style: 'normal',
|
|
519
|
-
children: [
|
|
520
|
-
{
|
|
521
|
-
_key: 'legal-p1-span',
|
|
522
|
-
_type: 'span',
|
|
523
|
-
text: 'This is a demo site, scaffolded by create-cogenta from the "portfolio" blueprint. No data collected here is real.',
|
|
524
|
-
marks: [],
|
|
525
|
-
},
|
|
526
|
-
],
|
|
527
|
-
markDefs: [],
|
|
528
|
-
},
|
|
529
|
-
],
|
|
530
|
-
},
|
|
531
|
-
],
|
|
532
|
-
},
|
|
533
|
-
];
|
|
534
|
-
}
|
|
535
|
-
export const PORTFOLIO_RECOMMENDED_AGENTS = [
|
|
536
|
-
{
|
|
537
|
-
name: 'performanceAgent',
|
|
538
|
-
package: '@cogenta/agents-builtin',
|
|
539
|
-
reason: 'Catches oversized project cover images before they slow the work grid down.',
|
|
540
|
-
},
|
|
541
|
-
{
|
|
542
|
-
name: 'seoAgent',
|
|
543
|
-
package: '@cogenta/agents-builtin',
|
|
544
|
-
reason: 'Audits project pages so each one is findable on its own, not only from the grid.',
|
|
545
|
-
},
|
|
546
|
-
];
|
|
547
|
-
/**
|
|
548
|
-
* `portfolio`'s own starting skin (`starting-skins.js`) — asserted present
|
|
549
|
-
* with a real check, not a `!`, since `STARTING_SKINS` is keyed by
|
|
550
|
-
* blueprint id and TypeScript cannot see that this particular key is
|
|
551
|
-
* always populated.
|
|
552
|
-
*/
|
|
553
|
-
function portfolioPalette() {
|
|
554
|
-
const skin = STARTING_SKINS.portfolio;
|
|
555
|
-
if (skin === undefined) {
|
|
556
|
-
throw new CogentaError({
|
|
557
|
-
code: 'BLUEPRINT_REGISTRY_CORRUPT',
|
|
558
|
-
message: 'STARTING_SKINS.portfolio is missing.',
|
|
559
|
-
hint: 'The "portfolio" entry must stay declared in starting-skins.ts for this blueprint to render its demo art.',
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
return skin.color;
|
|
563
|
-
}
|
|
564
|
-
/**
|
|
565
|
-
* Procedural demo visuals (L25 D1/D5): an "editorial mark on dark" hero
|
|
566
|
-
* backdrop (the strongest of the flat families against this theme's own
|
|
567
|
-
* dark-mode identity), one distinct cover per project (`coverSeed`, so the
|
|
568
|
-
* eight read as genuinely different compositions rather than a palette
|
|
569
|
-
* repeated eight times), five client marks, and two avatars — one for the
|
|
570
|
-
* home `quote`, one for the `testimonial` — kept as two separate specs
|
|
571
|
-
* rather than one shared avatar, since a real site would rarely show the
|
|
572
|
-
* same face twice on the same page.
|
|
573
|
-
*/
|
|
574
|
-
/** Bundled photography (`assets/photos/portfolio/`), keyed by the same slug `PORTFOLIO_DEMO_PROJECTS` uses. */
|
|
575
|
-
const PORTFOLIO_PROJECT_PHOTOS = {
|
|
576
|
-
'northwind-rebrand': 'portfolio/northwind-rebrand.jpg',
|
|
577
|
-
'contoso-mobile-app': 'portfolio/contoso-mobile-app.jpg',
|
|
578
|
-
'fabrikam-annual-report': 'portfolio/fabrikam-annual-report.jpg',
|
|
579
|
-
'litware-signage-system': 'portfolio/litware-signage.jpg',
|
|
580
|
-
'tailspin-streaming-identity': 'portfolio/tailspin-streaming.jpg',
|
|
581
|
-
'adatum-publishing-house': 'portfolio/adatum-publishing.jpg',
|
|
582
|
-
'wingtip-terminal-wayfinding': 'portfolio/wingtip-wayfinding.jpg',
|
|
583
|
-
'proseware-product-launch': 'portfolio/proseware-launch.jpg',
|
|
584
|
-
};
|
|
585
|
-
export const PORTFOLIO_MEDIA_SPECS = [
|
|
586
|
-
{
|
|
587
|
-
name: 'hero',
|
|
588
|
-
spec: heroArt(portfolioPalette(), 'ink', 4),
|
|
589
|
-
alt: 'A design studio workspace',
|
|
590
|
-
photo: 'portfolio/hero.jpg',
|
|
591
|
-
},
|
|
592
|
-
// The About page's own `mediaFigure` (L26 D2) — the same bundled workspace
|
|
593
|
-
// photograph as the home hero, a second real photo of the studio floor
|
|
594
|
-
// rather than a literal reuse of the hero's own media id, since a real
|
|
595
|
-
// studio site frequently shows its own workspace more than once.
|
|
596
|
-
{
|
|
597
|
-
name: 'about-figure',
|
|
598
|
-
spec: heroArt(portfolioPalette(), 'ink', 17),
|
|
599
|
-
alt: 'The studio floor, mid-review',
|
|
600
|
-
photo: 'portfolio/hero.jpg',
|
|
601
|
-
},
|
|
602
|
-
...PORTFOLIO_DEMO_PROJECTS.map((demo) => {
|
|
603
|
-
const photo = PORTFOLIO_PROJECT_PHOTOS[demo.slug];
|
|
604
|
-
return {
|
|
605
|
-
name: `project-${demo.slug}`,
|
|
606
|
-
spec: coverArt(portfolioPalette(), demo.coverSeed),
|
|
607
|
-
alt: `${demo.title}, cover composition`,
|
|
608
|
-
...(photo === undefined ? {} : { photo }),
|
|
609
|
-
};
|
|
610
|
-
}),
|
|
611
|
-
...[1, 2, 3, 4, 5].map((n) => ({
|
|
612
|
-
name: `logo-${n}`,
|
|
613
|
-
spec: logoArt(n * 13),
|
|
614
|
-
alt: 'An abstract client mark',
|
|
615
|
-
})),
|
|
616
|
-
{
|
|
617
|
-
name: 'quote-avatar',
|
|
618
|
-
spec: avatarArt(portfolioPalette(), 201),
|
|
619
|
-
alt: 'An abstract avatar composition, standing in for a client photo',
|
|
620
|
-
},
|
|
621
|
-
{
|
|
622
|
-
name: 'testimonial-avatar',
|
|
623
|
-
spec: avatarArt(portfolioPalette(), 202),
|
|
624
|
-
alt: 'An abstract avatar composition, standing in for a client photo',
|
|
625
|
-
},
|
|
626
|
-
];
|
|
627
|
-
/** Header/footer navigation and the header "Let's talk" button (L25 D4) — "Work"/"Services" are real, working in-page anchors into the home page's own blocks; "About"/"Contact"/"Legal" are real pages. */
|
|
628
|
-
export const PORTFOLIO_MENUS = {
|
|
629
|
-
header: [
|
|
630
|
-
{ label: 'Work', url: '/#home-work' },
|
|
631
|
-
{ label: 'Services', url: '/#home-services' },
|
|
632
|
-
{ label: 'About', url: '/about' },
|
|
633
|
-
{ label: 'Contact', url: '/contact' },
|
|
634
|
-
],
|
|
635
|
-
footer: [
|
|
636
|
-
{ label: 'Work', url: '/#home-work' },
|
|
637
|
-
{ label: 'About', url: '/about' },
|
|
638
|
-
{ label: 'Contact', url: '/contact' },
|
|
639
|
-
{ label: 'Legal', url: '/legal' },
|
|
640
|
-
],
|
|
641
|
-
headerAction: { label: "Let's talk", url: '/contact' },
|
|
642
|
-
};
|
|
643
|
-
export const PORTFOLIO_SITE_SETTINGS = {
|
|
644
|
-
'general.tagline': 'Brand, product and editorial design, made in the open.',
|
|
645
|
-
'general.socialLinks': [
|
|
646
|
-
{ label: 'Instagram', url: 'https://instagram.com/example' },
|
|
647
|
-
{ label: 'Dribbble', url: 'https://dribbble.com/example' },
|
|
648
|
-
{ label: 'LinkedIn', url: 'https://linkedin.com/company/example' },
|
|
649
|
-
],
|
|
650
|
-
'general.footerNote': 'Studio, est. 2017 · Replies within two working days.',
|
|
651
|
-
};
|
|
652
|
-
/**
|
|
653
|
-
* Inserts the `portfolio` blueprint's demo content through the real
|
|
654
|
-
* `ContentStore` — never mocked (house rule). Each project's `coverImage`
|
|
655
|
-
* and the home page's hero/logos/quote/testimonial media come from
|
|
656
|
-
* `ctx.media` (`seedDemoMedia`/`PORTFOLIO_MEDIA_SPECS`) — absent (a caller
|
|
657
|
-
* that never ran `seedDemoMedia`) simply leaves those fields unset, since
|
|
658
|
-
* none of them is `required` on its collection/block.
|
|
659
|
-
*/
|
|
660
|
-
async function seedPortfolioDemoContent(ctx) {
|
|
661
|
-
const { db, defaultLocale, adminId, media } = ctx;
|
|
662
|
-
const projectStore = createContentStore({ db, collection: project, defaultLocale });
|
|
663
|
-
const pageStore = createContentStore({ db, collection: page, defaultLocale });
|
|
664
|
-
for (const demo of PORTFOLIO_DEMO_PROJECTS) {
|
|
665
|
-
const coverImage = media[`project-${demo.slug}`];
|
|
666
|
-
await projectStore.create({
|
|
667
|
-
status: 'published',
|
|
668
|
-
createdBy: adminId,
|
|
669
|
-
values: {
|
|
670
|
-
title: demo.title,
|
|
671
|
-
slug: demo.slug,
|
|
672
|
-
summary: demo.summary,
|
|
673
|
-
role: demo.role,
|
|
674
|
-
year: demo.year,
|
|
675
|
-
...(coverImage === undefined ? {} : { coverImage }),
|
|
676
|
-
},
|
|
677
|
-
blocks: {
|
|
678
|
-
blocks: [roleYearPanelBlock(demo), projectNarrativeBlock(demo)].map(toBlockZoneEntry),
|
|
679
|
-
},
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
for (const demo of buildPortfolioDemoPages(media)) {
|
|
683
|
-
await pageStore.create({
|
|
684
|
-
status: 'published',
|
|
685
|
-
createdBy: adminId,
|
|
686
|
-
values: { title: demo.title, slug: demo.slug },
|
|
687
|
-
blocks: { blocks: demo.blocks.map(toBlockZoneEntry) },
|
|
688
|
-
});
|
|
689
|
-
}
|
|
690
|
-
}
|
|
691
|
-
export const portfolioContentPack = {
|
|
692
|
-
collections: PORTFOLIO_COLLECTIONS,
|
|
693
|
-
recommendedAgents: PORTFOLIO_RECOMMENDED_AGENTS,
|
|
694
|
-
seedDemoContent: seedPortfolioDemoContent,
|
|
695
|
-
defaultTheme: '@cogenta/theme-portfolio',
|
|
696
|
-
menus: PORTFOLIO_MENUS,
|
|
697
|
-
siteSettings: PORTFOLIO_SITE_SETTINGS,
|
|
698
|
-
mediaSpecs: PORTFOLIO_MEDIA_SPECS,
|
|
699
|
-
};
|
|
700
|
-
//# sourceMappingURL=portfolio.js.map
|