@foldspace_npm/harness 0.1.31 → 0.1.33
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/CLAUDE.md +27 -8
- package/README.md +25 -2
- package/bin/ask.mjs +16 -145
- package/bin/attach.mjs +2 -0
- package/bin/cli.mjs +22 -0
- package/bin/inject.mjs +2 -14
- package/bin/record.mjs +412 -0
- package/bin/share.mjs +63 -0
- package/package.json +1 -1
- package/src/attach-daemon.mjs +18 -0
- package/src/badge-auto.mjs +38 -1
- package/src/cdp-client.mjs +7 -1
- package/src/chrome-binary.mjs +28 -0
- package/src/cli-help.mjs +1 -0
- package/src/cli-registry.mjs +97 -3
- package/src/init.mjs +170 -16
- package/src/panel-driver.mjs +283 -0
- package/src/session-events.mjs +1 -0
- package/src/upgrade.mjs +87 -1
- package/src/video/README.md +29 -0
- package/src/video/assets/foldspace-mark.svg +27 -0
- package/src/video/assets/logo-dark.svg +68 -0
- package/src/video/assets/logo-light.svg +56 -0
- package/src/video/brand.mjs +46 -0
- package/src/video/compose.mjs +476 -0
- package/src/video/composer/composer.mjs +554 -0
- package/src/video/composer/index.html +13 -0
- package/src/video/fonts/OFL.txt +93 -0
- package/src/video/fonts/Poppins-ExtraBold.woff2 +0 -0
- package/src/video/fonts/Poppins-Regular.woff2 +0 -0
- package/src/video/fonts/Poppins-SemiBold.woff2 +0 -0
- package/src/video/recorder.mjs +331 -0
- package/src/video/scenes.mjs +253 -0
- package/src/video/share.mjs +284 -0
- package/src/video/timeline.mjs +726 -0
- package/src/video/vendor/mp4-muxer.LICENSE +21 -0
- package/src/video/vendor/mp4-muxer.min.mjs +5 -0
- package/templates/agent-starter/README.md +5 -0
- package/templates/agent-starter/claude-settings.json +1 -0
- package/templates/agent-starter/gitignore +3 -0
- package/templates/agent-starter/skills/share-a-video/SKILL.md +271 -0
package/src/session-events.mjs
CHANGED
package/src/upgrade.mjs
CHANGED
|
@@ -18,6 +18,8 @@ const instructionTemplatePath = path.join(
|
|
|
18
18
|
"agent-starter",
|
|
19
19
|
"CLAUDE.md",
|
|
20
20
|
);
|
|
21
|
+
// Flat in the template (as claude-settings.json is); .claude/skills/ in a project.
|
|
22
|
+
const skillsTemplateRoot = path.join(packageRoot, "templates", "agent-starter", "skills");
|
|
21
23
|
const tokenPattern = /\{\{([A-Z0-9_]+)\}\}/g;
|
|
22
24
|
|
|
23
25
|
export function resolveProjectDir(env = process.env, cwd = process.cwd()) {
|
|
@@ -208,6 +210,84 @@ export function refreshInstructionStub(projectDir, options = {}) {
|
|
|
208
210
|
return { written: true, backedUp, importPresent: true };
|
|
209
211
|
}
|
|
210
212
|
|
|
213
|
+
/** Every file of the harness's skills: [{ rel, source }], rel under .claude/skills/. */
|
|
214
|
+
function shippedSkillFiles(skillsRoot) {
|
|
215
|
+
const files = [];
|
|
216
|
+
if (!fs.existsSync(skillsRoot)) return files;
|
|
217
|
+
const walk = (relative) => {
|
|
218
|
+
for (const entry of fs.readdirSync(path.join(skillsRoot, relative), { withFileTypes: true })) {
|
|
219
|
+
const rel = path.join(relative, entry.name);
|
|
220
|
+
if (entry.isDirectory()) walk(rel);
|
|
221
|
+
else if (entry.isFile()) files.push({ rel, source: path.join(skillsRoot, rel) });
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
walk("");
|
|
225
|
+
return files;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* The skills this harness ships, written into the project's .claude/skills/
|
|
230
|
+
* from the installed package: `init` writes them with the rest of the
|
|
231
|
+
* template, and an upgrade brings them up to date the same way. Only the
|
|
232
|
+
* harness's own skill folders are written; any other skill there is left
|
|
233
|
+
* alone. They carry no template tokens, so they are copied as they are. A
|
|
234
|
+
* copy that differs is kept beside it as <file>.bak first - it may hold the
|
|
235
|
+
* human's own edits. Returns { written, backedUp }, paths relative to the
|
|
236
|
+
* project.
|
|
237
|
+
*/
|
|
238
|
+
export function refreshProjectSkills(projectDir, { skillsRoot = skillsTemplateRoot } = {}) {
|
|
239
|
+
const written = [];
|
|
240
|
+
const backedUp = [];
|
|
241
|
+
for (const { rel, source } of shippedSkillFiles(skillsRoot)) {
|
|
242
|
+
const target = path.join(projectDir, ".claude", "skills", rel);
|
|
243
|
+
const content = fs.readFileSync(source);
|
|
244
|
+
let current = null;
|
|
245
|
+
try {
|
|
246
|
+
current = fs.readFileSync(target);
|
|
247
|
+
} catch {
|
|
248
|
+
current = null;
|
|
249
|
+
}
|
|
250
|
+
if (current && current.equals(content)) continue;
|
|
251
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
252
|
+
if (current) {
|
|
253
|
+
fs.writeFileSync(`${target}.bak`, current);
|
|
254
|
+
backedUp.push(path.join(".claude", "skills", `${rel}.bak`));
|
|
255
|
+
}
|
|
256
|
+
fs.writeFileSync(target, content);
|
|
257
|
+
written.push(path.join(".claude", "skills", rel));
|
|
258
|
+
}
|
|
259
|
+
return { written, backedUp };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Write any of the harness's skill files the project does not have yet, and
|
|
264
|
+
* touch nothing else. Every command runs it: `upgrade --yes` runs the harness
|
|
265
|
+
* version that was installed before, which may come from before the skills
|
|
266
|
+
* shipped, so the first upgrade to this one cannot write them - the next
|
|
267
|
+
* command does. Only in a Foldspace project (foldspace.dev.json). Returns the
|
|
268
|
+
* paths written; never throws.
|
|
269
|
+
*/
|
|
270
|
+
export function ensureProjectSkills(projectDir, { skillsRoot = skillsTemplateRoot } = {}) {
|
|
271
|
+
const written = [];
|
|
272
|
+
try {
|
|
273
|
+
if (!fs.existsSync(path.join(projectDir, "foldspace.dev.json"))) return written;
|
|
274
|
+
for (const { rel, source } of shippedSkillFiles(skillsRoot)) {
|
|
275
|
+
const target = path.join(projectDir, ".claude", "skills", rel);
|
|
276
|
+
if (fs.existsSync(target)) continue;
|
|
277
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
278
|
+
fs.copyFileSync(source, target);
|
|
279
|
+
written.push(path.join(".claude", "skills", rel));
|
|
280
|
+
}
|
|
281
|
+
} catch {
|
|
282
|
+
// a read-only or odd project folder: the manual names the shipped copy too
|
|
283
|
+
}
|
|
284
|
+
return written;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function skillsReport(result) {
|
|
288
|
+
return result.backedUp.length ? { skills: result.written, skillsBackedUp: result.backedUp } : { skills: result.written };
|
|
289
|
+
}
|
|
290
|
+
|
|
211
291
|
function cachePath(projectDir) {
|
|
212
292
|
return path.join(projectDir, ".foldspace-dev", "harness-update.json");
|
|
213
293
|
}
|
|
@@ -483,6 +563,7 @@ export async function runUpgrade(argv, options = {}) {
|
|
|
483
563
|
instructionsImport: true,
|
|
484
564
|
refreshed: refreshed.written,
|
|
485
565
|
backedUp: refreshed.backedUp,
|
|
566
|
+
...skillsReport(refreshProjectSkills(projectDir, options)),
|
|
486
567
|
next: refreshed.written
|
|
487
568
|
? "Imported platform instructions from the installed harness."
|
|
488
569
|
: report.next,
|
|
@@ -525,7 +606,9 @@ export async function runUpgrade(argv, options = {}) {
|
|
|
525
606
|
}
|
|
526
607
|
|
|
527
608
|
if (!shouldInstall && !shouldRefresh) {
|
|
528
|
-
|
|
609
|
+
// Already on the latest: --yes still brings the skills up to date, for a
|
|
610
|
+
// project whose last upgrade ran a harness from before they shipped.
|
|
611
|
+
return parsed.yes ? { ...report, ...skillsReport(refreshProjectSkills(projectDir, options)) } : report;
|
|
529
612
|
}
|
|
530
613
|
|
|
531
614
|
if (shouldInstall) {
|
|
@@ -541,6 +624,8 @@ export async function runUpgrade(argv, options = {}) {
|
|
|
541
624
|
if (shouldRefresh) {
|
|
542
625
|
refreshed = refreshInstructionStub(projectDir, options);
|
|
543
626
|
}
|
|
627
|
+
// After the install, this package's folder holds the new version's files.
|
|
628
|
+
const skills = skillsReport(refreshProjectSkills(projectDir, options));
|
|
544
629
|
|
|
545
630
|
const nextReport = {
|
|
546
631
|
...report,
|
|
@@ -551,6 +636,7 @@ export async function runUpgrade(argv, options = {}) {
|
|
|
551
636
|
instructionsImport: shouldRefresh ? true : report.instructionsImport,
|
|
552
637
|
refreshed: refreshed.written,
|
|
553
638
|
backedUp: refreshed.backedUp,
|
|
639
|
+
...skills,
|
|
554
640
|
};
|
|
555
641
|
nextReport.next = nextAction(nextReport);
|
|
556
642
|
return nextReport;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# src/video - what `foldspace record` is made of
|
|
2
|
+
|
|
3
|
+
| File | What it is |
|
|
4
|
+
|---|---|
|
|
5
|
+
| `recorder.mjs` | One scene: a new conversation, the question typed key by key, the answer, recorded with Chrome's screencast into `.foldspace-dev/record/` |
|
|
6
|
+
| `timeline.mjs` | Pure timing and layout: shortened waits, caption fades, dissolves, the caption card (its rows, the line wrapped evenly in two, the `*highlights*`), where the logo goes, colour contrast. Runs in Node (tests) and in the composer page |
|
|
7
|
+
| `scenes.mjs` | `video/scenes.json`: checking it, the five capability names (one list: renaming a pill is a one-line change), each caption's "2 of 4", the file name |
|
|
8
|
+
| `compose.mjs` | The plan, a short-lived 127.0.0.1 server and a hidden Chrome that makes the MP4 |
|
|
9
|
+
| `composer/` | The page that draws each frame on a canvas, encodes H.264 with WebCodecs and muxes |
|
|
10
|
+
| `brand.mjs` | Foldspace's brand, as its product video's storyboard draws it - its one blue on black, white, one muted grey - the caption card's fill and edge, and the logo files. Never the app's colours |
|
|
11
|
+
|
|
12
|
+
Nothing is installed and nothing native runs: the video is encoded by the
|
|
13
|
+
Google Chrome that is already there.
|
|
14
|
+
|
|
15
|
+
## Vendored, with their licences
|
|
16
|
+
|
|
17
|
+
- `vendor/mp4-muxer.min.mjs` - mp4-muxer 5.2.2 (MIT, `vendor/mp4-muxer.LICENSE`),
|
|
18
|
+
from the npm registry (tarball sha512
|
|
19
|
+
`dhozjTywI0h2qFzeShagt8YYw811fh1XlwiDCE2f6Aeqf6xG2CyuShoSa5E0AZDO8pPF0JOZ3wOmWBNWIGdSpQ==`),
|
|
20
|
+
`build/mp4-muxer.mjs` minified with esbuild. Not a dependency: the file is
|
|
21
|
+
loaded only by the composer page.
|
|
22
|
+
- `fonts/Poppins-{Regular,SemiBold,ExtraBold}.woff2` - Poppins from the
|
|
23
|
+
google/fonts repository (`ofl/poppins`), converted from TTF to WOFF2 unchanged
|
|
24
|
+
otherwise; SIL Open Font License 1.1 in `fonts/OFL.txt`.
|
|
25
|
+
- `assets/` - the official Foldspace logos, unchanged, from the website
|
|
26
|
+
(foldspace-ai/marketing-site): `logo-dark.svg` (src/assets: the white
|
|
27
|
+
wordmark, for dark backgrounds), `logo-light.svg` (src/assets: the dark
|
|
28
|
+
wordmark, for light backgrounds) and `foldspace-mark.svg` (public/favicon.svg:
|
|
29
|
+
the blue mark).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg id="Foldspace_icon_1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 39.89 40">
|
|
3
|
+
<defs>
|
|
4
|
+
<style>
|
|
5
|
+
.cls-1 {
|
|
6
|
+
fill: #fff;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.cls-2 {
|
|
10
|
+
fill: #1842EF;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.cls-3 {
|
|
14
|
+
opacity: .5;
|
|
15
|
+
}
|
|
16
|
+
</style>
|
|
17
|
+
</defs>
|
|
18
|
+
<path class="cls-2" d="M21.31,18.6S23.08,0,18.84,0C14.31,0,0,14.51,0,18.84s18.57,2.55,18.57,2.55c0,0-1.76,18.61,2.47,18.61,4.54,0,18.84-14.51,18.84-18.84s-18.57-2.55-18.57-2.55Z"/>
|
|
19
|
+
<g class="cls-3">
|
|
20
|
+
<path class="cls-1" d="M21.32,18.6c-3.54.26-15.04.77-15.04-1.8,0-2.77,7.23-9.57,15.06-12.2.78,5.51-.02,14.01-.02,14.01h0Z"/>
|
|
21
|
+
<path class="cls-1" d="M18.57,21.39c3.54-.25,15.04-.77,15.04,1.8,0,2.77-7.23,9.57-15.06,12.2-.78-5.51.02-14,.02-14h0Z"/>
|
|
22
|
+
</g>
|
|
23
|
+
<g class="cls-3">
|
|
24
|
+
<path class="cls-1" d="M21.32,18.6c-2.36.13-7.66-1.08-7.66-2.66s4.7-4.41,8.03-5.37c0,4.19-.36,8.02-.36,8.02h0Z"/>
|
|
25
|
+
<path class="cls-1" d="M18.58,21.39c2.36-.14,7.67,1.07,7.67,2.66s-4.7,4.41-8.03,5.37c0-4.19.36-8.02.36-8.02h0Z"/>
|
|
26
|
+
</g>
|
|
27
|
+
</svg>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!-- Foldspace wordmark, white — for dark backgrounds. Cloned from logo-light.svg (the fixed
|
|
3
|
+
black export): identical geometry, wordmark and gradient effects inverted to white, and
|
|
4
|
+
the blue mark on the left left exactly as-is. -->
|
|
5
|
+
<svg id="Foldspace_logo_3" data-name="Foldspace logo 3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 383 40">
|
|
6
|
+
<defs>
|
|
7
|
+
<radialGradient id="radial-gradient-w" cx="-260.9" cy="-217.59" fx="-260.9" fy="-217.59" r="24.52" gradientTransform="matrix(1.999999, 0.002443, -0.000086, 0.07, 847.280029, 36.614626)" gradientUnits="userSpaceOnUse">
|
|
8
|
+
<stop offset="0" stop-color="#fff"/>
|
|
9
|
+
<stop offset=".14" stop-color="#fff" stop-opacity=".79"/>
|
|
10
|
+
<stop offset=".31" stop-color="#fff" stop-opacity=".56"/>
|
|
11
|
+
<stop offset=".48" stop-color="#fff" stop-opacity=".36"/>
|
|
12
|
+
<stop offset=".64" stop-color="#fff" stop-opacity=".2"/>
|
|
13
|
+
<stop offset=".78" stop-color="#fff" stop-opacity=".09"/>
|
|
14
|
+
<stop offset=".91" stop-color="#fff" stop-opacity=".02"/>
|
|
15
|
+
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
|
16
|
+
</radialGradient>
|
|
17
|
+
<radialGradient id="radial-gradient-w-2" cx="-42.52" cy="252.22" fx="-42.52" fy="252.22" r="24.52" gradientTransform="matrix(1.379999, 0.001686, -0.000061, 0.05, 385.178223, 6.85996)" href="#radial-gradient-w" xlink:href="#radial-gradient-w"/>
|
|
18
|
+
<radialGradient id="radial-gradient-w-3" cx="1130.48" cy="503.88" fx="1130.48" fy="503.88" r="2" gradientTransform="matrix(-3.531012, -0.924368, 1.219353, -2.668178, 3648.81282, 2350.485806)" gradientUnits="userSpaceOnUse">
|
|
19
|
+
<stop offset="0" stop-color="#fff"/>
|
|
20
|
+
<stop offset=".17" stop-color="#fff" stop-opacity=".78"/>
|
|
21
|
+
<stop offset=".4" stop-color="#fff" stop-opacity=".5"/>
|
|
22
|
+
<stop offset=".6" stop-color="#fff" stop-opacity=".29"/>
|
|
23
|
+
<stop offset=".78" stop-color="#fff" stop-opacity=".13"/>
|
|
24
|
+
<stop offset=".92" stop-color="#fff" stop-opacity=".04"/>
|
|
25
|
+
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
|
26
|
+
</radialGradient>
|
|
27
|
+
</defs>
|
|
28
|
+
<g id="Foldspace_logo_3_art" transform="matrix(1, 0, 0, 1, 26.598669, -202.805344)">
|
|
29
|
+
<g transform="matrix(1, 0, 0, 1, -26.598669, 202.805344)">
|
|
30
|
+
<path class="cls-5" d="M21.31,18.6S23.08,0,18.84,0C14.31,0,0,14.51,0,18.84s18.57,2.55,18.57,2.55c0,0-1.76,18.61,2.47,18.61,4.54,0,18.84-14.51,18.84-18.84s-18.57-2.55-18.57-2.55Z" style="fill: rgb(24, 66, 239);"/>
|
|
31
|
+
<g class="cls-6" style="opacity: 0.5;">
|
|
32
|
+
<path class="cls-1" d="M21.32,18.6c-3.54.26-15.04.77-15.04-1.8,0-2.77,7.23-9.57,15.06-12.2.78,5.51-.02,14.01-.02,14.01h0Z" style="fill: rgb(255, 255, 255);"/>
|
|
33
|
+
<path class="cls-1" d="M18.57,21.39c3.54-.25,15.04-.77,15.04,1.8,0,2.77-7.23,9.57-15.06,12.2-.78-5.51.02-14,.02-14h0Z" style="fill: rgb(255, 255, 255);"/>
|
|
34
|
+
</g>
|
|
35
|
+
<g class="cls-6" style="opacity: 0.5;">
|
|
36
|
+
<path class="cls-1" d="M21.32,18.6c-2.36.13-7.66-1.08-7.66-2.66s4.7-4.41,8.03-5.37c0,4.19-.36,8.02-.36,8.02h0Z" style="fill: rgb(255, 255, 255);"/>
|
|
37
|
+
<path class="cls-1" d="M18.58,21.39c2.36-.14,7.67,1.07,7.67,2.66s-4.7,4.41-8.03,5.37c0-4.19.36-8.02.36-8.02h0Z" style="fill: rgb(255, 255, 255);"/>
|
|
38
|
+
</g>
|
|
39
|
+
</g>
|
|
40
|
+
<g transform="matrix(1, 0, 0, 1, -26.598669, 202.805344)" fill="#fff">
|
|
41
|
+
<g>
|
|
42
|
+
<polygon points="53.77 30 55.91 30 55.91 21.21 68.29 21.21 68.29 19.07 53.77 19.06 53.77 30"/>
|
|
43
|
+
<rect x="53.77" y="10.28" width="19.72" height="2.14"/>
|
|
44
|
+
</g>
|
|
45
|
+
<path d="M94.82,10.28c-5.44,0-9.86,4.42-9.86,9.86s4.42,9.86,9.86,9.86,9.86-4.42,9.86-9.86-4.42-9.86-9.86-9.86ZM102.54,20.14c0,4.25-3.46,7.72-7.72,7.72s-7.72-3.46-7.72-7.72,3.46-7.72,7.72-7.72,7.72,3.46,7.72,7.72Z"/>
|
|
46
|
+
<polygon points="120.71 10.28 118.56 10.28 118.56 30 138.28 30 138.28 27.86 120.71 27.86 120.71 10.28"/>
|
|
47
|
+
<path d="M162.02,10.28h-9.86v2.14h9.86c4.25,0,7.72,3.46,7.72,7.72s-3.46,7.72-7.72,7.72h-9.86v2.14h9.86c5.44,0,9.86-4.42,9.86-9.86s-4.42-9.86-9.86-9.86Z"/>
|
|
48
|
+
<path d="M185.76,15.74c0,3.01,2.45,5.47,5.47,5.47h8.79c1.83,0,3.32,1.49,3.32,3.32s-1.49,3.32-3.32,3.32h-14.26v2.14h14.26c3.01,0,5.47-2.45,5.47-5.47s-2.45-5.47-5.47-5.47h-8.79c-1.83,0-3.32-1.49-3.32-3.32s1.49-3.32,3.32-3.32h14.26v-2.14h-14.26c-3.01,0-5.47,2.45-5.47,5.47Z"/>
|
|
49
|
+
<path d="M233.61,10.28h-14.26v2.14h14.26c1.83,0,3.32,1.49,3.32,3.32s-1.49,3.32-3.32,3.32h-14.26v10.93h2.15v-8.79h12.11c3.01,0,5.47-2.45,5.47-5.47s-2.45-5.47-5.47-5.47Z"/>
|
|
50
|
+
<polygon points="247.96 30 250.53 30 259.2 14.58 267.86 30 270.44 30 259.21 10 247.96 30"/>
|
|
51
|
+
<path d="M280.86,20.14c0,5.44,4.42,9.86,9.86,9.86h9.86v-2.14h-9.86c-4.25,0-7.72-3.46-7.72-7.72s3.46-7.72,7.72-7.72h9.86v-2.14h-9.86c-5.44,0-9.86,4.42-9.86,9.86Z"/>
|
|
52
|
+
<g>
|
|
53
|
+
<path class="cls-2" d="M 383.35 21.284 C 383.35 22.364 357.97 23.214 326.66 23.174 C 295.35 23.134 269.98 22.224 269.98 21.144 C 269.98 20.064 295.36 19.214 326.67 19.254 C 357.98 19.294 383.35 20.204 383.35 21.284 Z" style="fill: url("#radial-gradient-w");"/>
|
|
54
|
+
<path class="cls-4" d="M 365.688 19.379 C 365.688 20.169 348.148 20.789 326.508 20.769 C 304.868 20.739 287.328 20.079 287.338 19.289 C 287.338 18.499 304.878 17.879 326.518 17.899 C 348.158 17.929 365.698 18.589 365.688 19.379 Z" style="fill: url("#radial-gradient-w-2");"/>
|
|
55
|
+
<path d="M324.32,12.42h9.86v-2.14h-9.86c-5.44,0-9.86,4.42-9.86,9.86s4.42,9.86,9.86,9.86h9.86v-2.14h-9.86c-4.25,0-7.72-3.46-7.72-7.72s3.46-7.72,7.72-7.72Z"/>
|
|
56
|
+
<circle cx="326.67" cy="20.14" r="1.2"/>
|
|
57
|
+
</g>
|
|
58
|
+
</g>
|
|
59
|
+
</g>
|
|
60
|
+
<g id="svg-1" fill="#fff">
|
|
61
|
+
<g transform="matrix(1, 0, 0, 1, 55.221105, 59.088541)">
|
|
62
|
+
<g>
|
|
63
|
+
<path class="cls-3" d="M 264.114 -42.099 C 266.374 -44.822 292.155 -45.685 287.424 -34.392 C 284.798 -28.123 262.07 -35.124 261.921 -34.993" style="fill: url("#radial-gradient-w-3");"/>
|
|
64
|
+
<circle cx="271.424" cy="-38.941" r="1.2"/>
|
|
65
|
+
</g>
|
|
66
|
+
</g>
|
|
67
|
+
</g>
|
|
68
|
+
</svg>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!-- Foldspace wordmark, black — for light backgrounds. Source: foldspace-black-fixed-08-2026-1.svg,
|
|
3
|
+
normalized here so the artwork sits at the viewBox origin (0 0 383 40). -->
|
|
4
|
+
<svg id="Foldspace_logo_1" data-name="Foldspace logo 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 383 40">
|
|
5
|
+
<defs>
|
|
6
|
+
<radialGradient id="radial-gradient" cx="-260.9" cy="-217.59" fx="-260.9" fy="-217.59" r="24.52" gradientTransform="matrix(1.999999, 0.002443, -0.000086, 0.07, 847.280029, 36.614626)" gradientUnits="userSpaceOnUse">
|
|
7
|
+
<stop offset="0" stop-color="#000"/>
|
|
8
|
+
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
|
9
|
+
</radialGradient>
|
|
10
|
+
<radialGradient id="radial-gradient-2" cx="-42.52" cy="252.22" fx="-42.52" fy="252.22" r="24.52" gradientTransform="matrix(1.379999, 0.001686, -0.000061, 0.05, 385.178223, 6.85996)" href="#radial-gradient" xlink:href="#radial-gradient"/>
|
|
11
|
+
<radialGradient id="radial-gradient-3" cx="1130.48" cy="503.88" fx="1130.48" fy="503.88" r="2" gradientTransform="matrix(-3.531012, -0.924368, 1.219353, -2.668178, 3648.81282, 2350.485806)" gradientUnits="userSpaceOnUse">
|
|
12
|
+
<stop offset="0" stop-color="#000"/>
|
|
13
|
+
<stop offset="1" stop-color="#fff" stop-opacity="0"/>
|
|
14
|
+
</radialGradient>
|
|
15
|
+
</defs>
|
|
16
|
+
<g id="Foldspace_logo_1_art" transform="matrix(1, 0, 0, 1, 26.598669, -202.805344)">
|
|
17
|
+
<g transform="matrix(1, 0, 0, 1, -26.598669, 202.805344)">
|
|
18
|
+
<path class="cls-5" d="M21.31,18.6S23.08,0,18.84,0C14.31,0,0,14.51,0,18.84s18.57,2.55,18.57,2.55c0,0-1.76,18.61,2.47,18.61,4.54,0,18.84-14.51,18.84-18.84s-18.57-2.55-18.57-2.55Z" style="fill: rgb(24, 66, 239);"/>
|
|
19
|
+
<g class="cls-6" style="opacity: 0.5;">
|
|
20
|
+
<path class="cls-1" d="M21.32,18.6c-3.54.26-15.04.77-15.04-1.8,0-2.77,7.23-9.57,15.06-12.2.78,5.51-.02,14.01-.02,14.01h0Z" style="fill: rgb(255, 255, 255);"/>
|
|
21
|
+
<path class="cls-1" d="M18.57,21.39c3.54-.25,15.04-.77,15.04,1.8,0,2.77-7.23,9.57-15.06,12.2-.78-5.51.02-14,.02-14h0Z" style="fill: rgb(255, 255, 255);"/>
|
|
22
|
+
</g>
|
|
23
|
+
<g class="cls-6" style="opacity: 0.5;">
|
|
24
|
+
<path class="cls-1" d="M21.32,18.6c-2.36.13-7.66-1.08-7.66-2.66s4.7-4.41,8.03-5.37c0,4.19-.36,8.02-.36,8.02h0Z" style="fill: rgb(255, 255, 255);"/>
|
|
25
|
+
<path class="cls-1" d="M18.58,21.39c2.36-.14,7.67,1.07,7.67,2.66s-4.7,4.41-8.03,5.37c0-4.19.36-8.02.36-8.02h0Z" style="fill: rgb(255, 255, 255);"/>
|
|
26
|
+
</g>
|
|
27
|
+
</g>
|
|
28
|
+
<g transform="matrix(1, 0, 0, 1, -26.598669, 202.805344)">
|
|
29
|
+
<g>
|
|
30
|
+
<polygon points="53.77 30 55.91 30 55.91 21.21 68.29 21.21 68.29 19.07 53.77 19.06 53.77 30"/>
|
|
31
|
+
<rect x="53.77" y="10.28" width="19.72" height="2.14"/>
|
|
32
|
+
</g>
|
|
33
|
+
<path d="M94.82,10.28c-5.44,0-9.86,4.42-9.86,9.86s4.42,9.86,9.86,9.86,9.86-4.42,9.86-9.86-4.42-9.86-9.86-9.86ZM102.54,20.14c0,4.25-3.46,7.72-7.72,7.72s-7.72-3.46-7.72-7.72,3.46-7.72,7.72-7.72,7.72,3.46,7.72,7.72Z"/>
|
|
34
|
+
<polygon points="120.71 10.28 118.56 10.28 118.56 30 138.28 30 138.28 27.86 120.71 27.86 120.71 10.28"/>
|
|
35
|
+
<path d="M162.02,10.28h-9.86v2.14h9.86c4.25,0,7.72,3.46,7.72,7.72s-3.46,7.72-7.72,7.72h-9.86v2.14h9.86c5.44,0,9.86-4.42,9.86-9.86s-4.42-9.86-9.86-9.86Z"/>
|
|
36
|
+
<path d="M185.76,15.74c0,3.01,2.45,5.47,5.47,5.47h8.79c1.83,0,3.32,1.49,3.32,3.32s-1.49,3.32-3.32,3.32h-14.26v2.14h14.26c3.01,0,5.47-2.45,5.47-5.47s-2.45-5.47-5.47-5.47h-8.79c-1.83,0-3.32-1.49-3.32-3.32s1.49-3.32,3.32-3.32h14.26v-2.14h-14.26c-3.01,0-5.47,2.45-5.47,5.47Z"/>
|
|
37
|
+
<path d="M233.61,10.28h-14.26v2.14h14.26c1.83,0,3.32,1.49,3.32,3.32s-1.49,3.32-3.32,3.32h-14.26v10.93h2.15v-8.79h12.11c3.01,0,5.47-2.45,5.47-5.47s-2.45-5.47-5.47-5.47Z"/>
|
|
38
|
+
<polygon points="247.96 30 250.53 30 259.2 14.58 267.86 30 270.44 30 259.21 10 247.96 30"/>
|
|
39
|
+
<path d="M280.86,20.14c0,5.44,4.42,9.86,9.86,9.86h9.86v-2.14h-9.86c-4.25,0-7.72-3.46-7.72-7.72s3.46-7.72,7.72-7.72h9.86v-2.14h-9.86c-5.44,0-9.86,4.42-9.86,9.86Z"/>
|
|
40
|
+
<g>
|
|
41
|
+
<path class="cls-2" d="M 383.35 21.284 C 383.35 22.364 357.97 23.214 326.66 23.174 C 295.35 23.134 269.98 22.224 269.98 21.144 C 269.98 20.064 295.36 19.214 326.67 19.254 C 357.98 19.294 383.35 20.204 383.35 21.284 Z" style="fill: url("#radial-gradient");"/>
|
|
42
|
+
<path class="cls-4" d="M 365.688 19.379 C 365.688 20.169 348.148 20.789 326.508 20.769 C 304.868 20.739 287.328 20.079 287.338 19.289 C 287.338 18.499 304.878 17.879 326.518 17.899 C 348.158 17.929 365.698 18.589 365.688 19.379 Z" style="fill: url("#radial-gradient-2");"/>
|
|
43
|
+
<path d="M324.32,12.42h9.86v-2.14h-9.86c-5.44,0-9.86,4.42-9.86,9.86s4.42,9.86,9.86,9.86h9.86v-2.14h-9.86c-4.25,0-7.72-3.46-7.72-7.72s3.46-7.72,7.72-7.72Z"/>
|
|
44
|
+
<circle cx="326.67" cy="20.14" r="1.2"/>
|
|
45
|
+
</g>
|
|
46
|
+
</g>
|
|
47
|
+
</g>
|
|
48
|
+
<g id="svg-1">
|
|
49
|
+
<g transform="matrix(1, 0, 0, 1, 55.221105, 59.088541)">
|
|
50
|
+
<g>
|
|
51
|
+
<path class="cls-3" d="M 264.114 -42.099 C 266.374 -44.822 292.155 -45.685 287.424 -34.392 C 284.798 -28.123 262.07 -35.124 261.921 -34.993" style="fill: url("#radial-gradient-3");"/>
|
|
52
|
+
<circle cx="271.424" cy="-38.941" r="1.2"/>
|
|
53
|
+
</g>
|
|
54
|
+
</g>
|
|
55
|
+
</g>
|
|
56
|
+
</svg>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// The video's look: Foldspace's own brand, as its product video's storyboard
|
|
2
|
+
// draws it - never the app's colours. One accent, the brand blue, on pure
|
|
3
|
+
// black; white text, and one muted grey for the small lines. The logos are
|
|
4
|
+
// the website's own vector files, in assets/, in the brand kit's blue
|
|
5
|
+
// (#1842EF): the website's copies carried an older blue, #3247F2.
|
|
6
|
+
//
|
|
7
|
+
// No Node imports: the composer page loads this file too.
|
|
8
|
+
|
|
9
|
+
import { blendHex } from "./timeline.mjs";
|
|
10
|
+
|
|
11
|
+
export const BRAND = Object.freeze({
|
|
12
|
+
blue: "#1842EF", // the one accent
|
|
13
|
+
black: "#000000", // the opening and closing cards, and the bars around the product
|
|
14
|
+
muted: "#8E8E99", // small lines: "Built with", "2 OF 4", the question, the closing card's eyebrow and last line
|
|
15
|
+
white: "#FFFFFF",
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// The caption card: the storyboard's panel colour at 88%, so the product shows
|
|
19
|
+
// through a little - the most see-through it can be with the muted question
|
|
20
|
+
// still at 4.5:1 over a white app. In a letterboxed frame it stays on the
|
|
21
|
+
// picture (captionLayout), so it never crosses onto the black band.
|
|
22
|
+
export const CAPTION_FILL = Object.freeze({ color: "#0B0B0D", alpha: 0.88 });
|
|
23
|
+
export const CAPTION_EDGE = "rgba(255, 255, 255, 0.12)";
|
|
24
|
+
// The lightest it gets - over a white app - which is where light text on it
|
|
25
|
+
// is hardest to read.
|
|
26
|
+
export const CAPTION_BACKDROP_LIGHTEST = blendHex(CAPTION_FILL.color, CAPTION_FILL.alpha, "#FFFFFF");
|
|
27
|
+
// The opening and closing cards' background, and the bars around the product.
|
|
28
|
+
export const CARD_BG = BRAND.black;
|
|
29
|
+
|
|
30
|
+
// WCAG: 4.5:1 for small text; 3:1 for large text (the blue "Product Agent"
|
|
31
|
+
// line) and for rules. Blue on black is only about 3.06:1, so blue is never
|
|
32
|
+
// small text on black: only the large title line, the rule, and a fill
|
|
33
|
+
// behind white text - the pills and the caption's highlight (6.85:1).
|
|
34
|
+
export const TEXT_CONTRAST = 4.5;
|
|
35
|
+
export const LARGE_TEXT_CONTRAST = 3;
|
|
36
|
+
export const MARK_CONTRAST = 3;
|
|
37
|
+
|
|
38
|
+
// The logos, as the website ships them (viewBox 0 0 383 40 and 0 0 39.89 40),
|
|
39
|
+
// in the brand kit's blue.
|
|
40
|
+
export const LOGO_FILES = Object.freeze({
|
|
41
|
+
onDark: "logo-dark.svg", // white wordmark, blue mark
|
|
42
|
+
onLight: "logo-light.svg", // dark wordmark, blue mark
|
|
43
|
+
mark: "foldspace-mark.svg", // the blue mark alone (the website's favicon)
|
|
44
|
+
});
|
|
45
|
+
export const WORDMARK_RATIO = 383 / 40;
|
|
46
|
+
export const MARK_RATIO = 39.89 / 40;
|