@navipartner/demo-video 0.5.0 → 0.6.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/README.md +102 -105
- package/bin/args.d.mts +0 -3
- package/bin/args.mjs +1 -5
- package/bin/demo-video.mjs +5 -7
- package/bin/init.mjs +5 -10
- package/bin/pick.d.mts +1 -3
- package/bin/pick.mjs +65 -118
- package/bin/pitch.mjs +1 -1
- package/dist/{adapter-D_iU2dh2.d.ts → adapter-CDOXdUZI.d.ts} +0 -7
- package/dist/{chunk-BYAFDWPA.js → chunk-D7UYGNSF.js} +5 -66
- package/dist/index.d.ts +2 -6
- package/dist/index.js +1 -1
- package/dist/runner/index.d.ts +1 -1
- package/dist/runner/index.js +13 -13
- package/package.json +1 -1
- package/skills/np-demo-video/SKILL.md +47 -107
- package/skills/np-demo-video/linear.md +116 -0
- package/skills/np-demo-video/look.md +2 -4
- package/skills/np-demo-video/reference.md +8 -15
- package/skills/np-demo-video/pitch.md +0 -99
package/README.md
CHANGED
|
@@ -1,135 +1,132 @@
|
|
|
1
1
|
# @navipartner/demo-video
|
|
2
2
|
|
|
3
|
-
Marketing demo videos of
|
|
4
|
-
customer's brand.
|
|
3
|
+
Marketing demo videos of React apps.
|
|
5
4
|
|
|
6
|
-
- Filmed
|
|
7
|
-
-
|
|
5
|
+
- Filmed on the app's own e2e mocks.
|
|
6
|
+
- NaviPartner look: camera pushes, serif cards.
|
|
7
|
+
- NaviPartner's brand, the only one kept.
|
|
8
|
+
- Customer brands researched per video, never saved.
|
|
9
|
+
- Smooth 60 fps, frame by frame.
|
|
10
|
+
- pnpm repos, GitHub Actions, `@playwright/test` 1.59+.
|
|
8
11
|
|
|
9
12
|
## Setup
|
|
10
13
|
|
|
11
|
-
From the repo root:
|
|
12
|
-
|
|
13
14
|
npx --ignore-scripts @navipartner/demo-video init
|
|
14
15
|
pnpm install
|
|
15
16
|
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
|
28
|
-
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
-
|
|
62
|
-
`pitch.md`. It reads what the app needs (`brandNeeds`), researches the
|
|
63
|
-
brand from the customer's own website, and runs `demo-video pitch`, which
|
|
64
|
-
checks it, downloads the images and films at 30 fps. The MP4 and its
|
|
65
|
-
contact sheet are attached to the issue.
|
|
66
|
-
- **Nothing kept:** no commit, no PR, no brand in the repo. The session
|
|
67
|
-
works in `/tmp/pitch` and deletes it.
|
|
68
|
-
- **For that customer only:** Google Fonts stand-ins, and a person checks it
|
|
69
|
-
before sending. Any other use needs their permission and a brand in a PR.
|
|
70
|
-
- **Cost and time:** Linear AI credits, no API key. Linear's sandbox films
|
|
71
|
-
slowly: about 8× the video's length at 30 fps (a 75 s story ≈ 10 min).
|
|
72
|
-
- A `/demo-video … pitch …` comment on GitHub replies with these steps.
|
|
17
|
+
- Adds the skill, label and workflow.
|
|
18
|
+
- Finds apps with a `playwright.config`.
|
|
19
|
+
- One app: set up directly.
|
|
20
|
+
- Several apps: `init --app <folder>` each.
|
|
21
|
+
- Safe to re-run; `--dry-run` previews.
|
|
22
|
+
- Then ask Claude for a video.
|
|
23
|
+
|
|
24
|
+
## Get a video
|
|
25
|
+
|
|
26
|
+
| Ask | Who | You get |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| @Linear "video of checkout from np-pos for Northwind (northwind.example)" | Linear users | that story in Northwind's researched brand, attached |
|
|
29
|
+
| @Linear "video of checkout from np-pos" | Linear users | that story, NaviPartner brand, attached |
|
|
30
|
+
| `/demo-video` on a PR | members | primary story, from the PR branch |
|
|
31
|
+
| `/demo-video checkout` | members | that story |
|
|
32
|
+
| `/demo-video checkout in danish` | members | its translation |
|
|
33
|
+
| `/demo-video help` | members | menu of stories |
|
|
34
|
+
| `demo video` label on a PR | whoever labels | every changed story, each push |
|
|
35
|
+
| Actions → Demo video → Run workflow | repo writers | any story, any branch |
|
|
36
|
+
| `pnpm exec demo-video render` | developers | any story, locally |
|
|
37
|
+
|
|
38
|
+
- Requests only pick existing stories.
|
|
39
|
+
- New stories come via PRs.
|
|
40
|
+
- PR comments: first line is the request.
|
|
41
|
+
- Unknown names get the menu.
|
|
42
|
+
- Customer branding only through @Linear.
|
|
43
|
+
|
|
44
|
+
### In Linear
|
|
45
|
+
|
|
46
|
+
- Ask @Linear in plain words.
|
|
47
|
+
- Name the repo, story and customer.
|
|
48
|
+
- Follows the skill's `linear.md`.
|
|
49
|
+
- NaviPartner itself: 60 fps video.
|
|
50
|
+
- A customer: a pitch draft.
|
|
51
|
+
- MP4 and contact sheet attached.
|
|
52
|
+
- Nothing committed, nothing kept.
|
|
53
|
+
- Linear AI credits, no API key.
|
|
54
|
+
- Slow sandbox: ~15× video length.
|
|
55
|
+
|
|
56
|
+
### Pitch drafts
|
|
57
|
+
|
|
58
|
+
- Researched from the customer's own website.
|
|
59
|
+
- Checked, then filmed at 30 fps.
|
|
60
|
+
- Google Fonts stand-ins only.
|
|
61
|
+
- That customer only; check before sending.
|
|
62
|
+
- Other use needs their written permission.
|
|
73
63
|
|
|
74
64
|
## Stories and brands
|
|
75
65
|
|
|
76
|
-
- **Story:** `demo/stories/<name>.json`,
|
|
77
|
-
|
|
78
|
-
-
|
|
79
|
-
|
|
80
|
-
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
-
|
|
66
|
+
- **Story:** `demo/stories/<name>.json`, scenes as JSON.
|
|
67
|
+
- Built only from the mocks.
|
|
68
|
+
- One story per app is primary.
|
|
69
|
+
- **Translation:** its own story, e.g. `checkout-da.json`.
|
|
70
|
+
- Same flow, cards in that language.
|
|
71
|
+
- **Brands:** only `navipartner`, shipped with the engine.
|
|
72
|
+
- No `demo/brands/` in repos: `check` refuses it.
|
|
73
|
+
- Stories never name a brand.
|
|
74
|
+
- **Brand needs:** the adapter lists what a brand gives.
|
|
75
|
+
- Pitch research fills exactly those.
|
|
76
|
+
- The skill has fields and the look.
|
|
86
77
|
|
|
87
78
|
## Commands
|
|
88
79
|
|
|
89
|
-
|
|
90
|
-
them all).
|
|
80
|
+
Run in the app's folder: `pnpm exec demo-video <command>`.
|
|
91
81
|
|
|
92
82
|
| Command | Does |
|
|
93
83
|
|---|---|
|
|
94
|
-
| `
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
|
|
100
|
-
|
|
84
|
+
| `init` | sets up a repo or app |
|
|
85
|
+
| `list` | stories, primary first |
|
|
86
|
+
| `check` | dry run in seconds, names problems |
|
|
87
|
+
| `render` | MP4, contact sheet, ten stills |
|
|
88
|
+
| `record` / `encode` / `stills` | render in parts |
|
|
89
|
+
| `brand-needs` | what a brand must give |
|
|
90
|
+
| `pitch <proposal.json>` | pitch draft from a researched brand |
|
|
91
|
+
| `pick` | what a request means (workflows) |
|
|
92
|
+
|
|
93
|
+
| Flag | Means |
|
|
94
|
+
|---|---|
|
|
95
|
+
| `--story <name>` | default: primary story |
|
|
96
|
+
| `--draft` | 30 fps, twice as fast |
|
|
97
|
+
| `--app <dir>` | app folder, default `.` |
|
|
98
|
+
| `--json` | machine-readable output |
|
|
99
|
+
| `--brand`, `--brands <dir>` | used by `pitch` only |
|
|
101
100
|
|
|
102
|
-
|
|
103
|
-
- `--brand <name>`: default `navipartner`.
|
|
104
|
-
- `--draft`: 30 fps, about twice as fast, for tuning.
|
|
105
|
-
- `--brands <dir>`: a brands folder outside the repo, searched first.
|
|
101
|
+
Output lands in `demo/out/`. Failures keep Playwright's output in `demo/out/failed/`.
|
|
106
102
|
|
|
107
|
-
##
|
|
103
|
+
## Develop
|
|
108
104
|
|
|
109
105
|
pnpm install
|
|
110
|
-
pnpm test && pnpm typecheck && pnpm build
|
|
106
|
+
pnpm test && pnpm typecheck && pnpm lint && pnpm build
|
|
107
|
+
|
|
108
|
+
- CI runs tests, actionlint, smoke render.
|
|
111
109
|
|
|
112
|
-
##
|
|
110
|
+
## Release
|
|
113
111
|
|
|
114
|
-
1. Bump `version`
|
|
112
|
+
1. Bump `version` on main.
|
|
115
113
|
2. `git tag vX.Y.Z && git push origin vX.Y.Z`.
|
|
116
114
|
|
|
117
|
-
- CI publishes to npm
|
|
118
|
-
|
|
119
|
-
-
|
|
115
|
+
- CI publishes to npm (`latest`).
|
|
116
|
+
- Trusted publishing, no npm token.
|
|
117
|
+
- `v0` tag moves along.
|
|
118
|
+
- Manual `release.yml` run: `next` prerelease.
|
|
120
119
|
|
|
121
|
-
|
|
120
|
+
## Public package, private repo
|
|
122
121
|
|
|
123
|
-
-
|
|
124
|
-
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
- It can't catch names: keep customers and people out of the skill and the
|
|
128
|
-
README.
|
|
122
|
+
- `check-public.mjs` checks every tarball.
|
|
123
|
+
- Allowed files only, no source maps.
|
|
124
|
+
- No secrets, emails or local paths.
|
|
125
|
+
- Names aren't caught: keep customers out.
|
|
129
126
|
|
|
130
|
-
|
|
127
|
+
## Compatibility
|
|
131
128
|
|
|
132
|
-
- Projects pin the CLI
|
|
133
|
-
|
|
134
|
-
-
|
|
135
|
-
|
|
129
|
+
- Projects pin the CLI version.
|
|
130
|
+
- Their workflows follow `v0` immediately.
|
|
131
|
+
- Workflows must support CLI 0.4+.
|
|
132
|
+
- Breaking that means `v1`.
|
package/bin/args.d.mts
CHANGED
|
@@ -27,9 +27,6 @@ export interface StoryEntry {
|
|
|
27
27
|
name: string;
|
|
28
28
|
description: string | null;
|
|
29
29
|
primary: boolean;
|
|
30
|
-
/** A variant: the story it extends, and the brand it is for. */
|
|
31
|
-
extends?: string;
|
|
32
|
-
brand?: string | null;
|
|
33
30
|
}
|
|
34
31
|
export function listStories(demoDir: string): StoryEntry[];
|
|
35
32
|
export function resolveStoryboard(
|
package/bin/args.mjs
CHANGED
|
@@ -18,7 +18,7 @@ Without --story, check, render and record use the app's primary story.
|
|
|
18
18
|
brand-needs: what a brand must give this app (its adapter's brandNeeds).
|
|
19
19
|
Setup: demo-video init [--app <dir>] [--dry-run]
|
|
20
20
|
For a /demo-video request: demo-video pick --request "<text>" [--json] <app dir>...
|
|
21
|
-
For a pitch draft (the skill's
|
|
21
|
+
For a pitch draft (the skill's linear.md): demo-video pitch <proposal.json> [--app <dir>] [--story <name>] [--out <dir>] [--json]`;
|
|
22
22
|
|
|
23
23
|
export const resolveStoryboard = (value, cwd, demoDir) => {
|
|
24
24
|
if (value.endsWith(".json")) {
|
|
@@ -62,10 +62,6 @@ export const listStories = (demoDir) => {
|
|
|
62
62
|
name: f.slice(0, -".json".length),
|
|
63
63
|
description: story.description ?? null,
|
|
64
64
|
primary: story.primary === true,
|
|
65
|
-
// A variant: the base story's flow, for one brand.
|
|
66
|
-
...(story.extends
|
|
67
|
-
? { extends: story.extends, brand: story.brand ?? null }
|
|
68
|
-
: {}),
|
|
69
65
|
};
|
|
70
66
|
})
|
|
71
67
|
.sort(
|
package/bin/demo-video.mjs
CHANGED
|
@@ -136,7 +136,7 @@ const runPick = (argv) => {
|
|
|
136
136
|
const arg = argv[i];
|
|
137
137
|
if (arg === "--json") {
|
|
138
138
|
json = true;
|
|
139
|
-
} else if (["--request", "--app", "--story"
|
|
139
|
+
} else if (["--request", "--app", "--story"].includes(arg)) {
|
|
140
140
|
flags[arg.slice(2)] = argv[++i] ?? "";
|
|
141
141
|
} else {
|
|
142
142
|
dirs.push(arg);
|
|
@@ -144,7 +144,7 @@ const runPick = (argv) => {
|
|
|
144
144
|
}
|
|
145
145
|
if (dirs.length === 0) {
|
|
146
146
|
console.error(
|
|
147
|
-
'Usage: demo-video pick [--request "<text>" | --app <a> --story <s>
|
|
147
|
+
'Usage: demo-video pick [--request "<text>" | --app <a> --story <s>] [--json] <app dir>...',
|
|
148
148
|
);
|
|
149
149
|
return 2;
|
|
150
150
|
}
|
|
@@ -165,15 +165,13 @@ const runPick = (argv) => {
|
|
|
165
165
|
} else if (out.menu) {
|
|
166
166
|
console.log(out.markdown);
|
|
167
167
|
} else {
|
|
168
|
-
console.log(
|
|
169
|
-
`${out.app}: story ${out.story}, brand ${out.brand ?? "(the app's default)"}`,
|
|
170
|
-
);
|
|
168
|
+
console.log(`${out.app}: story ${out.story}`);
|
|
171
169
|
}
|
|
172
170
|
return 0;
|
|
173
171
|
};
|
|
174
172
|
|
|
175
173
|
// demo-video pitch <proposal.json> [--app <dir>] [--story <name>] [--out <dir>]
|
|
176
|
-
// [--json]: a pitch draft from a researched proposal (the skill's
|
|
174
|
+
// [--json]: a pitch draft from a researched proposal (the skill's linear.md).
|
|
177
175
|
// It builds the brand in a temp folder (pitch.mjs), checks the story with
|
|
178
176
|
// it, films it at 30 fps and moves the video and its contact sheet to --out
|
|
179
177
|
// (default: the proposal's folder). The brand and the repo's copies go.
|
|
@@ -345,7 +343,7 @@ const run = () => {
|
|
|
345
343
|
} else {
|
|
346
344
|
for (const s of stories) {
|
|
347
345
|
console.log(
|
|
348
|
-
`${s.primary ? "* " : " "}${s.name}${s.description ? `: ${s.description}` : ""}
|
|
346
|
+
`${s.primary ? "* " : " "}${s.name}${s.description ? `: ${s.description}` : ""}`,
|
|
349
347
|
);
|
|
350
348
|
}
|
|
351
349
|
}
|
package/bin/init.mjs
CHANGED
|
@@ -22,16 +22,15 @@ const engineVersion = () =>
|
|
|
22
22
|
|
|
23
23
|
export const CALLER_WORKFLOW = `name: Demo video
|
|
24
24
|
# The "${LABEL}" label films the stories a PR adds or changes. A comment
|
|
25
|
-
# "/demo-video [<story>]
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# Pitch drafts for a customer without a brand here are @Linear's (README).
|
|
25
|
+
# "/demo-video [<story>]" on a PR, or Actions → Demo video → Run workflow,
|
|
26
|
+
# films an existing story in NaviPartner's brand. A customer's branding: ask
|
|
27
|
+
# @Linear (the skill's linear.md); repos keep no brands.
|
|
29
28
|
on:
|
|
30
29
|
pull_request:
|
|
31
30
|
types: [labeled, synchronize]
|
|
32
31
|
issue_comment:
|
|
33
32
|
types: [created]
|
|
34
|
-
# Actions → Demo video → Run workflow: film a story
|
|
33
|
+
# Actions → Demo video → Run workflow: film a story.
|
|
35
34
|
workflow_dispatch:
|
|
36
35
|
inputs:
|
|
37
36
|
app:
|
|
@@ -40,9 +39,6 @@ on:
|
|
|
40
39
|
story:
|
|
41
40
|
description: "Story name (empty: the app's primary story)"
|
|
42
41
|
default: ""
|
|
43
|
-
brand:
|
|
44
|
-
description: "Brand (empty: the app's default)"
|
|
45
|
-
default: ""
|
|
46
42
|
permissions: {}
|
|
47
43
|
jobs:
|
|
48
44
|
render:
|
|
@@ -52,7 +48,7 @@ jobs:
|
|
|
52
48
|
pull-requests: write
|
|
53
49
|
uses: navipartner/demo-video/.github/workflows/render.yml@v0
|
|
54
50
|
request:
|
|
55
|
-
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'issue_comment' &&
|
|
51
|
+
if: github.event_name == 'workflow_dispatch' || (github.event_name == 'issue_comment' && github.event.issue.pull_request && startsWith(github.event.comment.body, '/demo-video'))
|
|
56
52
|
permissions:
|
|
57
53
|
contents: read
|
|
58
54
|
pull-requests: write
|
|
@@ -61,7 +57,6 @@ jobs:
|
|
|
61
57
|
with:
|
|
62
58
|
app: \${{ inputs.app }}
|
|
63
59
|
story: \${{ inputs.story }}
|
|
64
|
-
brand: \${{ inputs.brand }}
|
|
65
60
|
`;
|
|
66
61
|
|
|
67
62
|
const read = (file) =>
|
package/bin/pick.d.mts
CHANGED
|
@@ -4,13 +4,11 @@ export interface AppMenu {
|
|
|
4
4
|
dir: string;
|
|
5
5
|
name: string;
|
|
6
6
|
stories: StoryEntry[];
|
|
7
|
-
brands: string[];
|
|
8
7
|
}
|
|
9
8
|
export type Pick =
|
|
10
9
|
| {
|
|
11
10
|
app: string;
|
|
12
11
|
story: string;
|
|
13
|
-
brand: string | null;
|
|
14
12
|
menu?: undefined;
|
|
15
13
|
}
|
|
16
14
|
| { menu: true; reason: string; apps?: AppMenu[] };
|
|
@@ -19,5 +17,5 @@ export function pick(request: string, apps: AppMenu[]): Pick;
|
|
|
19
17
|
export function menuMarkdown(apps: AppMenu[], reason: string): string;
|
|
20
18
|
export function pickExact(
|
|
21
19
|
apps: AppMenu[],
|
|
22
|
-
names: { app?: string; story?: string
|
|
20
|
+
names: { app?: string; story?: string },
|
|
23
21
|
): Pick;
|