@homepages/dev-media 0.0.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.
- package/LICENSE +25 -0
- package/README.md +91 -0
- package/assets/library/bluebird_logo_dark.svg +5 -0
- package/assets/library/bluebird_logo_light.svg +5 -0
- package/assets/library/elena_marsh.png +0 -0
- package/assets/library/james_donovan.png +0 -0
- package/assets/library/marco_bianchi.png +0 -0
- package/assets/library/marcus_webb.png +0 -0
- package/assets/library/ngozi_okafor.png +0 -0
- package/assets/library/okane_logo_dark.svg +5 -0
- package/assets/library/okane_logo_light.svg +5 -0
- package/assets/library/priya_chandran.png +0 -0
- package/assets/photo/pearl_street_01_exterior_front.jpg +0 -0
- package/assets/photo/pearl_street_02_living.jpg +0 -0
- package/assets/photo/pearl_street_03_dining.jpg +0 -0
- package/assets/photo/pearl_street_04_kitchen.jpg +0 -0
- package/assets/photo/pearl_street_05_primary_bedroom.jpg +0 -0
- package/assets/photo/pearl_street_06_second_bedroom.jpg +0 -0
- package/assets/photo/pearl_street_07_primary_bath.jpg +0 -0
- package/assets/photo/pearl_street_08_powder_room.jpg +0 -0
- package/assets/photo/pearl_street_09_balcony.png +0 -0
- package/assets/photo/pearl_street_10_rooftop_deck.png +0 -0
- package/assets/photo/queensway_01_lobby.jpg +0 -0
- package/assets/photo/queensway_02_gym_a.png +0 -0
- package/assets/photo/queensway_03_gym_b.png +0 -0
- package/assets/photo/queensway_04_living_a.jpg +0 -0
- package/assets/photo/queensway_05_living_b.jpg +0 -0
- package/assets/photo/queensway_06_kitchen_a.jpg +0 -0
- package/assets/photo/queensway_07_kitchen_b.jpg +0 -0
- package/assets/photo/queensway_08_dining.jpg +0 -0
- package/assets/photo/queensway_09_living_c.jpg +0 -0
- package/assets/photo/queensway_10_living_d.jpg +0 -0
- package/assets/photo/queensway_11_living_e.png +0 -0
- package/assets/photo/queensway_12_living_f.png +0 -0
- package/assets/photo/queensway_13_master_bedroom.jpg +0 -0
- package/assets/photo/queensway_14_master_bath_a.jpg +0 -0
- package/assets/photo/queensway_15_master_bath_b.jpg +0 -0
- package/assets/photo/queensway_16_common_bath.png +0 -0
- package/assets/photo/queensway_17_entrance.png +0 -0
- package/assets/photo/queensway_18_patio.png +0 -0
- package/assets/photo/queensway_19_outdoor_deck.jpg +0 -0
- package/assets/photo/queensway_20_exterior_hero.png +0 -0
- package/assets/photo/tuscan_road_01_exterior_front.png +0 -0
- package/assets/photo/tuscan_road_02_living.png +0 -0
- package/assets/photo/tuscan_road_03_kitchen.png +0 -0
- package/assets/photo/tuscan_road_04_bedroom.png +0 -0
- package/assets/photo/tuscan_road_05_backyard.png +0 -0
- package/assets/plan/pearl_street_unit04_main.png +0 -0
- package/assets/plan/pearl_street_unit04_upper.png +0 -0
- package/assets/plan/queensway_2br_11_l1.png +0 -0
- package/assets/plan/queensway_2br_2_l1.png +0 -0
- package/assets/plan/queensway_2br_4_l1.png +0 -0
- package/assets/plan/queensway_2br_6_l1.png +0 -0
- package/assets/plan/queensway_2br_8_l1.png +0 -0
- package/assets/plan/queensway_3br_10_l1.png +0 -0
- package/assets/plan/queensway_3br_10_l2.png +0 -0
- package/assets/plan/queensway_3br_1_l1.png +0 -0
- package/assets/plan/queensway_3br_1_l2.png +0 -0
- package/assets/plan/queensway_3br_5_l1.png +0 -0
- package/assets/plan/queensway_3br_5_l2.png +0 -0
- package/assets/plan/queensway_4br_12_l1.png +0 -0
- package/assets/plan/tuscan_road_main_floor.png +0 -0
- package/assets/video/pearl_street_tour-poster.png +0 -0
- package/assets/video/pearl_street_tour.mp4 +0 -0
- package/assets/video/queensway_walkthrough-poster.png +0 -0
- package/assets/video/queensway_walkthrough.mp4 +0 -0
- package/index.d.ts +34 -0
- package/index.js +29 -0
- package/manifest.json +940 -0
- package/package.json +43 -0
- package/selection.json +741 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
HomePages Dev Media License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HomePages
|
|
4
|
+
|
|
5
|
+
This package ("@homepages/dev-media") ships two kinds of bytes under `assets/`:
|
|
6
|
+
|
|
7
|
+
1. Real property photography and floor plans, owned by HomePages. Each
|
|
8
|
+
included asset's source is cleared for public redistribution per
|
|
9
|
+
`selection.json`'s `cleared` block, reviewed and recorded by the
|
|
10
|
+
repository owner. HomePages grants anyone who obtains this package a
|
|
11
|
+
worldwide, royalty-free, non-exclusive license to use, copy, modify, and
|
|
12
|
+
redistribute these assets, including as part of a derived build, solely
|
|
13
|
+
for developing, testing, and demonstrating HomePages templates. This
|
|
14
|
+
license does not grant any trademark right, and does not extend to any
|
|
15
|
+
asset not shipped in this package (including any excluded, unlicensed
|
|
16
|
+
source held in the HomePages dev environment).
|
|
17
|
+
|
|
18
|
+
2. Procedurally generated placeholder images and video, produced by this
|
|
19
|
+
package's own generator (`scripts/generate-placeholders.mjs`) from no
|
|
20
|
+
external input. These contain no third-party or model-generated content
|
|
21
|
+
and are provided under the same terms as (1), without restriction.
|
|
22
|
+
|
|
23
|
+
THE ASSETS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED. IN NO EVENT SHALL HOMEPAGES BE LIABLE FOR ANY CLAIM, DAMAGES, OR
|
|
25
|
+
OTHER LIABILITY ARISING FROM THEIR USE.
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# @homepages/dev-media
|
|
2
|
+
|
|
3
|
+
The dev-only media pack template authoring resolves against: `manifest.json` indexing
|
|
4
|
+
every asset the fixture corpus can select, plus the bytes in `assets/`.
|
|
5
|
+
|
|
6
|
+
Allowlisted to publish, though it has not yet had its first `npm publish`. It
|
|
7
|
+
is a package rather than a `@homepages/template-kit` subpath because the kit
|
|
8
|
+
is a dependency of every workspace and CI install, and media bytes would tax
|
|
9
|
+
all of them.
|
|
10
|
+
|
|
11
|
+
## What is in here
|
|
12
|
+
|
|
13
|
+
| path | role |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `manifest.json` | generated pack index — assets + named media sets |
|
|
16
|
+
| `selection.json` | reviewed slot → source mapping, with per-asset provenance and rights |
|
|
17
|
+
| `assets/` | the bytes, `assets/<kind>/<slug>.<ext>` |
|
|
18
|
+
| `vendor/` | byte-vendored copies of the derived contract; never hand-edit |
|
|
19
|
+
| `scripts/` | the importer, generators, and the pack validator |
|
|
20
|
+
|
|
21
|
+
Ids are namespaced per id space (`dev_upl_`, `dev_efp_`, `dev_lib_`, `dev_vid_`) so a
|
|
22
|
+
dev id is impossible to mistake for a real one if it ever reaches a real `content_doc`.
|
|
23
|
+
|
|
24
|
+
`tags` are a contract, not decoration: fixtures select by tag and throw when a value
|
|
25
|
+
has no match, which is why `check` asserts the full tag vocabulary is covered.
|
|
26
|
+
|
|
27
|
+
## Two kinds of bytes
|
|
28
|
+
|
|
29
|
+
Most slots hold **real property media** imported from the dev environment; the rest hold
|
|
30
|
+
**generated placeholders** — a labelled frame at the asset's true dimensions.
|
|
31
|
+
|
|
32
|
+
`selection.json` decides which is which. It maps a brief slot to a source object in the
|
|
33
|
+
dev uploads bucket and records that source's provenance and rights clearance. It is
|
|
34
|
+
committed and reviewed by a human: querying the dev database at build time would let the
|
|
35
|
+
pack's contents shift whenever that database changed, silently, with no version
|
|
36
|
+
recording it. Reviewed once, the pack regenerates byte-identically from S3 forever.
|
|
37
|
+
|
|
38
|
+
A slot with no entry falls back to its placeholder. That is a permanent state for some
|
|
39
|
+
slots and a temporary one for others:
|
|
40
|
+
|
|
41
|
+
- `sparseSingleFamily` keeps placeholders **by design** — it exercises the corpus's
|
|
42
|
+
missing-content branches, where a labelled frame is the honest representation.
|
|
43
|
+
- The library logos and headshots depict fictional brands and fictional people, so no
|
|
44
|
+
source could supply them.
|
|
45
|
+
- A handful of photo slots (gym, patio, balcony) have no matching content in the dev
|
|
46
|
+
environment yet and fill automatically once it exists.
|
|
47
|
+
|
|
48
|
+
Imported bytes are the platform's existing 2048px `image_web_variant` derivatives,
|
|
49
|
+
copied verbatim. No re-encoding: the derivatives already match the brief's width, and an
|
|
50
|
+
encoder here would drift against the platform's own ladder.
|
|
51
|
+
|
|
52
|
+
**Rights are enforced, not just recorded.** The importer refuses to fetch any selection
|
|
53
|
+
whose provenance is not listed in the file's `cleared` block, so adding a source without
|
|
54
|
+
clearing it fails the import rather than shipping quietly.
|
|
55
|
+
|
|
56
|
+
`npm run check` reports the real-vs-placeholder split explicitly. Coverage alone cannot:
|
|
57
|
+
it is asserted against the inventory rather than against a source, so an import that
|
|
58
|
+
silently resolved nothing would still come out green.
|
|
59
|
+
|
|
60
|
+
## Commands
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
npm run check # validate: schema, inventory coverage, files resolve, true dimensions
|
|
64
|
+
npm run import # fetch the selected sources from S3 (needs dev AWS credentials)
|
|
65
|
+
npm run generate # regenerate placeholders + manifest, prune superseded files
|
|
66
|
+
npm run generate:videos # also re-encode the two MP4s — needs Docker (no ffmpeg on the dev host)
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Re-authoring the selection (needs the dev Postgres container) is a deliberate,
|
|
70
|
+
hand-run step — never part of the build:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
node scripts/derive-selection.mjs > selection.proposed.json # review, clear rights, commit
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Dimensions and the responsive ladder
|
|
77
|
+
|
|
78
|
+
Production emits rungs `[320,640,960,1200,1600,2048]` across avif/webp/jpeg. The dev
|
|
79
|
+
server instead serves the source file at every rung URL while the manifest declares
|
|
80
|
+
each rung's true size, so the emitted `srcset` is structurally identical to production
|
|
81
|
+
and every URL resolves. The only divergence is transferred bytes — a performance
|
|
82
|
+
property, not a layout or authoring one. This is why a manifest that lies about
|
|
83
|
+
intrinsic dimensions is a real defect and `check` fails on it.
|
|
84
|
+
|
|
85
|
+
The brief's `min_width` is a separate thing from the declared dimensions: it states the
|
|
86
|
+
ladder ceiling the corpus binds a slot at, while the manifest states what the bytes
|
|
87
|
+
actually are. Every imported floor plan is narrower than the 1024px the brief asks for —
|
|
88
|
+
the dev environment's extracted plans run 341–876px — so their top rung upscales. This
|
|
89
|
+
is an accepted gap, not a defect: floor plans are line art and read acceptably upscaled,
|
|
90
|
+
and the alternative was leaving every plan slot synthetic. `check` prints one
|
|
91
|
+
`under-floor` warning per affected asset so the gap stays visible.
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 120" role="img" aria-label="Bluebird Realty Group">
|
|
2
|
+
<rect width="480" height="120" fill="#22262c"/>
|
|
3
|
+
<circle cx="60" cy="60" r="28" fill="none" stroke="#f4f6f8" stroke-width="6"/>
|
|
4
|
+
<text x="108" y="70" font-family="Helvetica, Arial, sans-serif" font-size="26" fill="#f4f6f8">Bluebird Realty Group</text>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 120" role="img" aria-label="Bluebird Realty Group">
|
|
2
|
+
<rect width="480" height="120" fill="#f4f6f8"/>
|
|
3
|
+
<circle cx="60" cy="60" r="28" fill="none" stroke="#22262c" stroke-width="6"/>
|
|
4
|
+
<text x="108" y="70" font-family="Helvetica, Arial, sans-serif" font-size="26" fill="#22262c">Bluebird Realty Group</text>
|
|
5
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 120" role="img" aria-label="Okane Marketing">
|
|
2
|
+
<rect width="480" height="120" fill="#22262c"/>
|
|
3
|
+
<circle cx="60" cy="60" r="28" fill="none" stroke="#f4f6f8" stroke-width="6"/>
|
|
4
|
+
<text x="108" y="70" font-family="Helvetica, Arial, sans-serif" font-size="26" fill="#f4f6f8">Okane Marketing</text>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 120" role="img" aria-label="Okane Marketing">
|
|
2
|
+
<rect width="480" height="120" fill="#f4f6f8"/>
|
|
3
|
+
<circle cx="60" cy="60" r="28" fill="none" stroke="#22262c" stroke-width="6"/>
|
|
4
|
+
<text x="108" y="70" font-family="Helvetica, Arial, sans-serif" font-size="26" fill="#22262c">Okane Marketing</text>
|
|
5
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** The four library kinds the editor's media picker lists. */
|
|
2
|
+
export type MediaKind = "photo" | "plan" | "library" | "video";
|
|
3
|
+
|
|
4
|
+
export interface MediaAsset {
|
|
5
|
+
/** Namespaced dev id (`dev_upl_`/`dev_efp_`/`dev_lib_`/`dev_vid_`). */
|
|
6
|
+
id: string;
|
|
7
|
+
kind: MediaKind;
|
|
8
|
+
/** Picker tile label — a display name, not a filename. */
|
|
9
|
+
name: string;
|
|
10
|
+
caption: string;
|
|
11
|
+
/** The selection contract: fixtures select by tag and throw when a value is absent. */
|
|
12
|
+
tags: Record<string, string>;
|
|
13
|
+
/** Pack-relative path to the source bytes. */
|
|
14
|
+
file: string;
|
|
15
|
+
/** True intrinsic dimensions of `file`. Absent for vector assets. */
|
|
16
|
+
width?: number;
|
|
17
|
+
height?: number;
|
|
18
|
+
/** `video` only. */
|
|
19
|
+
poster?: string;
|
|
20
|
+
duration_ms?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface MediaManifest {
|
|
24
|
+
version: 1;
|
|
25
|
+
license?: { spdx: string; source: string; notes?: string };
|
|
26
|
+
assets: MediaAsset[];
|
|
27
|
+
/** Named media sets keyed to the kit's fixture scenarios. */
|
|
28
|
+
sets: Record<string, Partial<Record<MediaKind, string[]>>>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export declare const manifest: MediaManifest;
|
|
32
|
+
export declare function getAsset(id: string): MediaAsset | null;
|
|
33
|
+
export declare function listByKind(kind: MediaKind): MediaAsset[];
|
|
34
|
+
export declare function resolveAssetPath(packRelative: string): string;
|
package/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Buildless on purpose: the package is data, and a compile step over a JSON index
|
|
2
|
+
// would only add a way for the shipped manifest to differ from the checked-in one.
|
|
3
|
+
|
|
4
|
+
import { readFileSync } from "node:fs";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const PACK_ROOT = new URL("./", import.meta.url);
|
|
8
|
+
|
|
9
|
+
/** The pack index. Generated from the derived inventory — see scripts/generate-manifest.mjs. */
|
|
10
|
+
export const manifest = JSON.parse(readFileSync(new URL("manifest.json", PACK_ROOT), "utf8"));
|
|
11
|
+
|
|
12
|
+
const BY_ID = new Map(manifest.assets.map((asset) => [asset.id, asset]));
|
|
13
|
+
|
|
14
|
+
export function getAsset(id) {
|
|
15
|
+
return BY_ID.get(id) ?? null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function listByKind(kind) {
|
|
19
|
+
return manifest.assets.filter((asset) => asset.kind === kind);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Absolute path for a pack-relative reference (an asset's `file` or `poster`).
|
|
24
|
+
* Callers hand these to a static file server or read them directly; the pack never
|
|
25
|
+
* assumes a URL scheme, because the dev server owns that mapping.
|
|
26
|
+
*/
|
|
27
|
+
export function resolveAssetPath(packRelative) {
|
|
28
|
+
return fileURLToPath(new URL(packRelative, PACK_ROOT));
|
|
29
|
+
}
|