@getvitops/cli 1.0.0 → 2.1.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/CHANGELOG.md +335 -0
- package/README.md +52 -0
- package/dist/cli.mjs +875 -73
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
- package/skill/SKILL.md +57 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,340 @@
|
|
|
1
1
|
# @getvitops/cli
|
|
2
2
|
|
|
3
|
+
## 2.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 20e518e: Add `vitops indexing` — tell search engines about a deploy, instead of opening Search Console by hand.
|
|
8
|
+
|
|
9
|
+
Configure it in your site config under `seo.indexing`, then run it after deploying:
|
|
10
|
+
|
|
11
|
+
```jsonc
|
|
12
|
+
"seo": {
|
|
13
|
+
"indexing": {
|
|
14
|
+
"indexNow": { "key": "…" },
|
|
15
|
+
"searchConsole": { "siteUrl": "sc-domain:acme.ca" },
|
|
16
|
+
"priorityUrls": ["https://acme.ca/", "https://acme.ca/services"]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
vitops indexing --dry # print the plan, make no requests
|
|
23
|
+
vitops indexing # submit
|
|
24
|
+
vitops indexing --check # a day or two later: did Google actually index them?
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
It reads your sitemap, diffs it against the previous run, and submits only what changed —
|
|
28
|
+
pinging **IndexNow** and re-submitting your sitemap through the **Search Console API**.
|
|
29
|
+
`--check` inspects `priorityUrls` and exits non-zero on a page Google hasn't indexed, so a
|
|
30
|
+
scheduled CI job can catch a page that quietly fell out of the index.
|
|
31
|
+
|
|
32
|
+
**Be clear-eyed about what this can do.** Google exposes no API that requests indexing — the
|
|
33
|
+
button in Search Console isn't available anywhere, URL Inspection is read-only, and the sitemap
|
|
34
|
+
ping endpoint was removed in 2023. IndexNow reaches Bing, Yandex, Naver, Seznam and Yep; Google
|
|
35
|
+
doesn't participate. So this automates every sanctioned step and then verifies the outcome; it
|
|
36
|
+
does not make Google re-index on demand, and nothing can. Google's Indexing API is deliberately
|
|
37
|
+
not wired: it's scoped to job postings and livestreams, and using it for ordinary pages violates
|
|
38
|
+
its terms.
|
|
39
|
+
|
|
40
|
+
Also new, and worth wiring at the same time — **`gitLastmod()` for real sitemap dates**:
|
|
41
|
+
|
|
42
|
+
```js
|
|
43
|
+
import vitops, { gitLastmod } from '@getvitops/astro';
|
|
44
|
+
vitops({ sitemap: { serialize: await gitLastmod() } });
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`@astrojs/sitemap` emits no `<lastmod>` by default, which means a crawler is told your pages
|
|
48
|
+
exist but never that one changed — and it's what lets `vitops indexing` submit a handful of URLs instead
|
|
49
|
+
of all of them. `gitLastmod()` derives each date from the source file's last commit, and leaves a
|
|
50
|
+
page alone rather than guessing (dynamic routes, ambiguous slugs, shallow clones): an inaccurate
|
|
51
|
+
`lastmod` is worse than none, because Google stops trusting the field site-wide.
|
|
52
|
+
|
|
53
|
+
Requires `fetch-depth: 0` in CI — the default shallow clone has no history to read, and it warns
|
|
54
|
+
when that's the case.
|
|
55
|
+
|
|
56
|
+
**Credentials.** IndexNow's key is public by design (it's served at `/<key>.txt` as the ownership
|
|
57
|
+
proof), so it lives in your config, and the Astro integration writes the file into `public/` for
|
|
58
|
+
you — `vitops indexing --new-key` generates one, `--write-key <dir>` writes it for non-Astro stacks.
|
|
59
|
+
Search Console needs a service account in `VITOPS_GSC_SERVICE_ACCOUNT` or
|
|
60
|
+
`GOOGLE_APPLICATION_CREDENTIALS`, added as an owner of the property; it's never read from the
|
|
61
|
+
config file.
|
|
62
|
+
|
|
63
|
+
Persist `.vitops/` between runs (a CI cache) — that's the changed-URL state, and without it every
|
|
64
|
+
run submits everything.
|
|
65
|
+
|
|
66
|
+
An environment whose `robots` policy says `noindex` is refused outright, so pointing this at
|
|
67
|
+
staging can't publish it to a search engine.
|
|
68
|
+
|
|
69
|
+
- 9bf975a: Add `vitops media` — encode raw video into web-ready outputs, instead of hand-rolling an ffmpeg
|
|
70
|
+
script per project.
|
|
71
|
+
|
|
72
|
+
Keep unprocessed video in a `raw/` directory and run:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
vitops media --raw raw --out src/assets/processed
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Each source becomes three files: **VP9/WebM**, an **H.264/MP4** fallback, and a **JPG poster**.
|
|
79
|
+
Import them like any other asset, so your bundler content-hashes them:
|
|
80
|
+
|
|
81
|
+
```astro
|
|
82
|
+
---
|
|
83
|
+
import hero from '../assets/processed/hero.webm';
|
|
84
|
+
import poster from '../assets/processed/hero.jpg';
|
|
85
|
+
---
|
|
86
|
+
<video poster={poster.src} autoplay muted loop playsinline>
|
|
87
|
+
<source src={hero} type="video/webm" />
|
|
88
|
+
</video>
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
In Astro it's an integration option — `vitops({ css, media: { raw: 'raw', out: 'src/assets/processed' } })`
|
|
92
|
+
— and it runs in the same pass that generates your CSS. `@getvitops/vite` gains a matching `media`
|
|
93
|
+
option, and `@getvitops/utils/media` exports `processMedia()` for anything else.
|
|
94
|
+
|
|
95
|
+
Defaults: capped at 1920px wide, CRF 32, audio dropped (the common case is a muted autoplay loop),
|
|
96
|
+
poster from frame 0. All of them are flags. `--dry` prints exactly what a run would do.
|
|
97
|
+
|
|
98
|
+
**Runs are cached**, on source content plus encode settings, in `.vitops/media-manifest.json` — a
|
|
99
|
+
24 MB clip that took 88 seconds the first time takes 0.14 seconds the second. A missing output
|
|
100
|
+
re-encodes; a corrupt manifest re-encodes everything. Neither ever reads as "already done", because
|
|
101
|
+
that failure is silent and no rebuild would fix it.
|
|
102
|
+
|
|
103
|
+
**Commit the outputs and the manifest.** A fresh CI clone has neither and would re-encode from
|
|
104
|
+
scratch; committing both means CI never needs ffmpeg. It also keeps your history clean — ffmpeg
|
|
105
|
+
output isn't byte-reproducible across versions, so a CI re-encode would rewrite every video on any
|
|
106
|
+
toolchain bump. Use `--force` when you mean to re-encode.
|
|
107
|
+
|
|
108
|
+
**`ffmpeg` is an external tool, not an npm dependency** — install it yourself (`brew install
|
|
109
|
+
ffmpeg`, `apt install ffmpeg`, `winget install Gyan.FFmpeg`). The command fails without it rather
|
|
110
|
+
than skipping: a page referencing a video that was never encoded is broken, not degraded.
|
|
111
|
+
|
|
112
|
+
Two defaults worth knowing about. The MP4 exists because older iOS and the in-app webviews inside
|
|
113
|
+
social apps still don't decode VP9, and it's written with `+faststart` — without which a browser
|
|
114
|
+
downloads the whole file before showing frame one. And a poster taken from frame 0 is often black
|
|
115
|
+
on a clip that fades in; that's `--poster-time`, not a bug.
|
|
116
|
+
|
|
117
|
+
### Patch Changes
|
|
118
|
+
|
|
119
|
+
- Updated dependencies [20e518e]
|
|
120
|
+
- Updated dependencies [9bf975a]
|
|
121
|
+
- @getvitops/generator@2.1.0
|
|
122
|
+
- @getvitops/utils@2.1.0
|
|
123
|
+
|
|
124
|
+
## 2.0.0
|
|
125
|
+
|
|
126
|
+
### Minor Changes
|
|
127
|
+
|
|
128
|
+
- bf453b0: Anywhere the toolchain takes a config, that file may now be a `design-system.json` **or** the larger
|
|
129
|
+
site config that embeds one.
|
|
130
|
+
|
|
131
|
+
If you keep your tokens inside a `company.json` / `site.json`, you had to maintain a second file for
|
|
132
|
+
the tooling's sake — or duplicate your whole token set, because a site config must carry a complete
|
|
133
|
+
`designSystem`. Now you point the same option at the file you already have:
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
// astro.config.mjs
|
|
137
|
+
getvitops({ css: { input: 'company.json', format: 'css' } });
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
vitops generate --input company.json --format css --out dist
|
|
142
|
+
vitops lint --input company.json --src src
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The design system is taken from `designSystem.themes[theme]` with its `extends` chain resolved —
|
|
146
|
+
`defaultTheme`, else `default`, else whatever `--theme` / the `theme` option names. Nothing changes
|
|
147
|
+
for a plain `design-system.json`; the two are told apart by shape, not by filename, so you can call
|
|
148
|
+
the file anything.
|
|
149
|
+
|
|
150
|
+
**A site config also supplies the site-level facts generation reads**, so the path is declared once
|
|
151
|
+
rather than per option:
|
|
152
|
+
- `designSystem.defaultColorScheme: "system"` — emits the `prefers-color-scheme` block, which is
|
|
153
|
+
what makes `<color-scheme-toggle>`'s System position do anything.
|
|
154
|
+
- `legal.*.enabled` — renders the documents. `legal: {}` is now the whole declaration; `legal.input`
|
|
155
|
+
is optional.
|
|
156
|
+
- `icons.sprite` — builds `icons.svg`.
|
|
157
|
+
- `fonts` — `fonts: true` reads the families from it.
|
|
158
|
+
|
|
159
|
+
Each of those is already gated on a field in that config, so nothing new appears unless the config
|
|
160
|
+
asks for it. An explicit `site` option still wins when the two are genuinely different files.
|
|
161
|
+
|
|
162
|
+
Also:
|
|
163
|
+
- **`vitops validate` routes on the file's shape.** Pointed at a site config it used to report a
|
|
164
|
+
single `unrecognized_keys` for `designSystem` and nothing about the file's actual contents — a
|
|
165
|
+
wrong answer that reads like a right one. It now validates a site config as one, including the
|
|
166
|
+
cross-field integrity JSON Schema can't express, and checks every theme resolves to a complete
|
|
167
|
+
design system (`validateSite` only checked that the `extends` chain resolved, not what it resolved
|
|
168
|
+
to).
|
|
169
|
+
- **`generate()` gained `theme` and `siteEnv`.** The A/B variant for `siteEnv` is applied before the
|
|
170
|
+
theme is selected, so a variant can override tokens.
|
|
171
|
+
- **The theme editor's Save to source follows the design system into a site config.** Its patch is
|
|
172
|
+
design-system-relative, so the dev server merges into that subtree and writes only the surrounding
|
|
173
|
+
file whole. It locates the subtree in the raw on-disk object rather than the normalised one, so an
|
|
174
|
+
author who wrote either `designSystem` shorthand still gets their own keys edited.
|
|
175
|
+
|
|
176
|
+
- 04a51d8: Icons: one semantic vocabulary across icon sets, with the bundle derived from your source.
|
|
177
|
+
|
|
178
|
+
Name icons by meaning — `<Icon name="menu" />` resolves to `fa7-solid:bars`, `ph:list` or
|
|
179
|
+
`lucide:menu` depending on the set you configure, so swapping sets is a config edit rather than a
|
|
180
|
+
find-and-replace. A name containing `:` still passes through untouched, which is the escape hatch
|
|
181
|
+
for a set-specific glyph.
|
|
182
|
+
|
|
183
|
+
**New `icons` option on `getvitops()`.** Configures the sets once per site and, under
|
|
184
|
+
`output: 'server'`, derives astro-icon's `include` map by scanning your source — the list most
|
|
185
|
+
projects end up maintaining by hand. On a static build no `include` is passed at all, because
|
|
186
|
+
astro-icon is already zero-config there and a list could only drop a glyph the scan couldn't see.
|
|
187
|
+
Names you declare that don't resolve fail the build; names only the scan found just warn; names
|
|
188
|
+
computed at runtime are reported with file and line so you can declare them.
|
|
189
|
+
|
|
190
|
+
**New `<Icon />` component**, and a real fix with it: `Popover`, `Details` and `Drawer` imported
|
|
191
|
+
`astro-icon/components` at module scope, so the _optional_ peer was resolved whether or not an icon
|
|
192
|
+
ever rendered — hard-failing anyone who hadn't installed it. Engines now load dynamically. The
|
|
193
|
+
per-component `iconResolver` prop still works but is deprecated in favour of the integration option.
|
|
194
|
+
|
|
195
|
+
**New SVG sprite** (`icons.sprite: true` in your site config) for consumers that can't run an icon
|
|
196
|
+
integration — Bricks/WordPress, EmDash renderers, plain HTML. `<use href="…/icons.svg#ph--list">`,
|
|
197
|
+
no JavaScript and no icon-API request. Every semantic name also gets a set-independent `icon-<name>`
|
|
198
|
+
alias, so sprite markup survives an icon-set change. WordPress gets `vitops_icon()`, a
|
|
199
|
+
`[vitops_icon]` shortcode and a **Vitops → Icon** Bricks element.
|
|
200
|
+
|
|
201
|
+
**New `vitops icons`** command: reports which icons your source uses, which names couldn't be
|
|
202
|
+
resolved, and which are computed at runtime; `--sprite` builds the sprite, `--json` for CI.
|
|
203
|
+
|
|
204
|
+
**Renamed, and completed to all four directions.** Chevrons and arrows are the
|
|
205
|
+
one family whose _meaning is_ its direction, and that direction is physical, not
|
|
206
|
+
logical — a chevron in a details marker points down in every writing mode. So they
|
|
207
|
+
are named for where they point: `expand-more`/`expand-less` →
|
|
208
|
+
`chevron-down`/`chevron-up`, and `arrow-forward`/`arrow-back` →
|
|
209
|
+
`arrow-right`/`arrow-left`. `chevron-left`/`chevron-right` keep their names.
|
|
210
|
+
`arrow-up`/`arrow-down` are new, so both families now cover all four directions.
|
|
211
|
+
`lightning` is new.
|
|
212
|
+
|
|
213
|
+
If you passed any of the old names to `<Icon />`, `resolveIcon` or an `icons`
|
|
214
|
+
config, update them. They fail loudly rather than silently: an unresolvable
|
|
215
|
+
declared name throws, and `vitops icons` reports scanned ones.
|
|
216
|
+
|
|
217
|
+
**Fixed three Font Awesome mappings that named no real glyph** and so rendered an
|
|
218
|
+
empty box: `login`/`logout` were mapped to `login`/`logout` (Font Awesome calls
|
|
219
|
+
them `right-to-bracket`/`right-from-bracket`) and `backup` to `backup`
|
|
220
|
+
(`cloud-arrow-up`). Every value in all four UI sets is now checked against the
|
|
221
|
+
installed collection.
|
|
222
|
+
|
|
223
|
+
**Phosphor (`ph`) joins the semantic map**, with all 83 names verified against the real icon set.
|
|
224
|
+
Phosphor keeps every weight in one collection and varies the name (`list`, `list-bold`), unlike Font
|
|
225
|
+
Awesome's per-weight collections, so `resolveIcon` and `generateIconInclude` gained a `weight`
|
|
226
|
+
option for sets shaped that way.
|
|
227
|
+
|
|
228
|
+
Fixes: `site.icons` was a closed object, so any icon collection not in its hand-written key list was
|
|
229
|
+
silently dropped during validation — your config passed and the icons never bundled. It accepts any
|
|
230
|
+
collection name now.
|
|
231
|
+
|
|
232
|
+
- bf453b0: `vitops lint` now catches hand-written CSS that re-implements a framework primitive.
|
|
233
|
+
|
|
234
|
+
The existing linter finds classes that resolve to nothing — you named something and it isn't there.
|
|
235
|
+
This is the costlier inverse, where the code **works**: a centred container written by hand, a
|
|
236
|
+
two-column split behind a media query, a flex row. Nothing is broken, so nothing ever surfaces it,
|
|
237
|
+
and the design system quietly stops being where those decisions live.
|
|
238
|
+
|
|
239
|
+
Three rules to start, each requiring the combination that makes the intent unambiguous
|
|
240
|
+
(`margin-inline: auto` alone is not a centred track):
|
|
241
|
+
|
|
242
|
+
| Found | Suggested |
|
|
243
|
+
| ----------------------------------------------------------- | ------------------------------------------------------------------------- |
|
|
244
|
+
| `max-inline-size: var(--width-*)` + `margin-inline: auto` | `.centered`, widening a child with `breakout` / `spotlight` / `fullbleed` |
|
|
245
|
+
| `grid-template-columns: 1fr 2fr` inside a `min-width` query | `md-split-1-2` — or `@md:split-1-2` in the tailwind format |
|
|
246
|
+
| `display: flex` + `align-items: center` | `flex items-center`, or `.cluster-start` |
|
|
247
|
+
|
|
248
|
+
**Findings now carry a severity, and suggestions do not fail the command.** These are judgement
|
|
249
|
+
calls, and a reuse hint that broke CI on the day it shipped would be a worse defect than the drift it
|
|
250
|
+
reports. `--strict` promotes them for anyone who wants the ratchet. Everything the linter reported
|
|
251
|
+
before is an `error` and still exits 1.
|
|
252
|
+
|
|
253
|
+
`.css` files and `<style>` blocks are now read (the class linter only ever looked at markup). The
|
|
254
|
+
generated stylesheet is skipped by its `GENERATED … do not edit by hand` banner rather than by path —
|
|
255
|
+
the `css` format writes `styles.css` into `src/styles`, squarely inside what `--src` scans, and
|
|
256
|
+
linting it made the framework report `.split` as reinventing `.split`.
|
|
257
|
+
|
|
258
|
+
Two other gaps closed:
|
|
259
|
+
- **The format-spelling check ran in one direction only.** `md-split-1-2` in a tailwind project was
|
|
260
|
+
caught; `@md:split-1-2` in a css/bricks project was not, because variant stripping removed the
|
|
261
|
+
`@md:` regardless of format and left a bare class that matched nothing. The same silent no-op, in
|
|
262
|
+
the other direction, unreported. Stripping is format-aware now.
|
|
263
|
+
- **`grid-auto` and the whole `m-*` rhythm family were missing from the tailwind format entirely** —
|
|
264
|
+
not dropped via `TW_CLASH`, just never re-emitted when `layout.css` is skipped and a subset
|
|
265
|
+
re-emitted in its place. Unlike the `<bp>-` classes they have no Tailwind equivalent, and unlike a
|
|
266
|
+
misspelt class the linter cannot flag them, because they aren't anchored to your config. They emit
|
|
267
|
+
as `@utility` definitions now, keyed to the same `--rhythm-*` variables as the css format.
|
|
268
|
+
|
|
269
|
+
Run against this repo's own docs site, the rules found one real instance on the first pass; it is
|
|
270
|
+
fixed in the same change.
|
|
271
|
+
|
|
272
|
+
- bf453b0: Maskable favicons are now opaque, so they stop rendering as a logo in a black box.
|
|
273
|
+
|
|
274
|
+
`icon-mask.png` is declared `purpose: "maskable"` and `apple-touch-icon.png` is linked on every
|
|
275
|
+
page. Both sit a deliberately-inset logo on a larger canvas — that inset **is** the maskable safe
|
|
276
|
+
zone and was always correct — but the canvas was filled with `alpha: 0`. From any transparent
|
|
277
|
+
source that left 36% of `icon-mask.png` and 40% of `apple-touch-icon.png` transparent, on two files
|
|
278
|
+
whose entire contract is full bleed: the OS crops them to its own shape and composites the rest onto
|
|
279
|
+
whatever it likes, usually black. iOS discards alpha on the apple-touch-icon outright.
|
|
280
|
+
|
|
281
|
+
`backgroundColor` was already the obvious input for this and already plumbed — as far as the web
|
|
282
|
+
manifest, and no further. So the raster and the manifest disagreed about the very colour meant to
|
|
283
|
+
sit behind the icon.
|
|
284
|
+
|
|
285
|
+
Now:
|
|
286
|
+
- those two outputs are composited onto `backgroundColor`, defaulting to `#ffffff` — the same
|
|
287
|
+
default the manifest's `background_color` already used;
|
|
288
|
+
- `favicon.svg`, `icon-{16,32,192,512}.png` and `favicon.ico` keep the source's transparency, since
|
|
289
|
+
none of them is maskable;
|
|
290
|
+
- a transparent source with no `backgroundColor` set now warns, rather than silently inheriting
|
|
291
|
+
white — a dark logo would lose against it;
|
|
292
|
+
- `icon-192`/`icon-512` declare `purpose: "any"` explicitly. Omitting it was legal (unset means
|
|
293
|
+
"any"), but with a maskable present and nothing claiming "any", some launchers picked the maskable
|
|
294
|
+
— the one with the safe-zone inset — for slots that wanted a plain icon.
|
|
295
|
+
|
|
296
|
+
`vitops favicon` gained `--background <hex>`; it previously had no way to set this at all.
|
|
297
|
+
`getvitops({ favicon })` and the Vite plugin now forward the option they were already accepting.
|
|
298
|
+
|
|
299
|
+
### Patch Changes
|
|
300
|
+
|
|
301
|
+
- bf453b0: Say plainly that `fonts` in `design-system.json` is stacks only and loads nothing.
|
|
302
|
+
|
|
303
|
+
The field's description was complete and self-consistent — "raw font stacks by name, emitted as
|
|
304
|
+
`--font-<name>` tokens" — and told you nothing about where the `@font-face` comes from. Following
|
|
305
|
+
it literally leads to installing a `@fontsource*` package and importing its CSS in a layout, which
|
|
306
|
+
renders correctly and silently gives up subsetting, preload, and the `size-adjust` /
|
|
307
|
+
`ascent-override` fallback metrics: a CLS regression that looks like a working setup.
|
|
308
|
+
|
|
309
|
+
The field is unchanged. What changed is that four surfaces now name the boundary and point at the
|
|
310
|
+
fix — declare the family in Astro's `fonts:` config and point the token at its `cssVariable`:
|
|
311
|
+
- the `fonts` description in the JSON Schema, and therefore `vitops docs authoring`
|
|
312
|
+
- `SKILL.md`, which previously never mentioned fonts at all
|
|
313
|
+
- the generated `tailwind.css` header and its `@theme` fonts comment
|
|
314
|
+
- the generated `type-tokens.css` header (css format)
|
|
315
|
+
|
|
316
|
+
- Updated dependencies [4756788]
|
|
317
|
+
- Updated dependencies [bf453b0]
|
|
318
|
+
- Updated dependencies [bf453b0]
|
|
319
|
+
- Updated dependencies [bf453b0]
|
|
320
|
+
- Updated dependencies [bf453b0]
|
|
321
|
+
- Updated dependencies [bf453b0]
|
|
322
|
+
- Updated dependencies [bf453b0]
|
|
323
|
+
- Updated dependencies [bf453b0]
|
|
324
|
+
- Updated dependencies [bf453b0]
|
|
325
|
+
- Updated dependencies [04a51d8]
|
|
326
|
+
- Updated dependencies [bf453b0]
|
|
327
|
+
- Updated dependencies [bf453b0]
|
|
328
|
+
- Updated dependencies [bf453b0]
|
|
329
|
+
- Updated dependencies [bf453b0]
|
|
330
|
+
- Updated dependencies [bf453b0]
|
|
331
|
+
- Updated dependencies [bf453b0]
|
|
332
|
+
- Updated dependencies [bf453b0]
|
|
333
|
+
- Updated dependencies [bf453b0]
|
|
334
|
+
- Updated dependencies [bf453b0]
|
|
335
|
+
- @getvitops/generator@2.0.0
|
|
336
|
+
- @getvitops/utils@2.0.0
|
|
337
|
+
|
|
3
338
|
## 1.0.0
|
|
4
339
|
|
|
5
340
|
### Major Changes
|
package/README.md
CHANGED
|
@@ -25,6 +25,13 @@ npx vitops generate --format bricks,css --out dist
|
|
|
25
25
|
| `vitops favicon -i <svg\|png> -o <dir>` | generate a favicon set from a source image |
|
|
26
26
|
| `vitops agents [-o AGENTS.md] [--docs-dir <dir>]` | link the agent skill + AGENTS.md pointer |
|
|
27
27
|
| `vitops docs [topic] [--all]` | print live reference docs to stdout |
|
|
28
|
+
| `vitops indexing [--dry] [--check]` | tell search engines about a deploy |
|
|
29
|
+
| `vitops media [--raw <dir>] [-o <dir>]` | encode raw video to WebM + MP4 + poster |
|
|
30
|
+
|
|
31
|
+
Every `--input` takes a `design-system.json` **or** the larger site config that embeds one
|
|
32
|
+
(`company.json` / `site.json`) — told apart by shape, so name the file whatever you like. Add
|
|
33
|
+
`--theme <name>` to build a `designSystem.themes` entry other than the default. `vitops validate`
|
|
34
|
+
routes the same way and checks a site config as a site config.
|
|
28
35
|
|
|
29
36
|
## Using the output
|
|
30
37
|
|
|
@@ -93,6 +100,51 @@ vitops generate --input design-system.json --format design --out .
|
|
|
93
100
|
whenever your stylesheet goes somewhere other than the repo root. Regenerate it with your CSS; it's
|
|
94
101
|
derived from the same config, so it can't drift from what the browser gets.
|
|
95
102
|
|
|
103
|
+
## Video (`vitops media`)
|
|
104
|
+
|
|
105
|
+
Keep unprocessed video in a `raw/` directory and encode it into web-ready outputs:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
vitops media --raw raw --out src/assets/processed
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Each source becomes three files — VP9/WebM, an H.264/MP4 fallback, and a JPG poster frame:
|
|
112
|
+
|
|
113
|
+
```astro
|
|
114
|
+
---
|
|
115
|
+
import hero from '../assets/processed/hero.webm';
|
|
116
|
+
import poster from '../assets/processed/hero.jpg';
|
|
117
|
+
---
|
|
118
|
+
<video poster={poster.src} autoplay muted loop playsinline>
|
|
119
|
+
<source src={hero} type="video/webm" />
|
|
120
|
+
</video>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Outputs land under `src/` so your bundler content-hashes them and they can be served immutable,
|
|
124
|
+
while the raw sources stay outside anything it scans.
|
|
125
|
+
|
|
126
|
+
Defaults: capped at 1920px wide, CRF 32, audio dropped (the common case is a muted autoplay loop),
|
|
127
|
+
poster taken from frame 0. Every one is a flag — `--max-width`, `--crf`, `--audio`,
|
|
128
|
+
`--poster-time`, `--max-bitrate`, `--outputs`. `vitops media --dry` prints exactly what a run
|
|
129
|
+
would do without encoding anything.
|
|
130
|
+
|
|
131
|
+
**Needs `ffmpeg` on `PATH`.** It's an external tool, not an npm dependency — install it yourself
|
|
132
|
+
(`brew install ffmpeg`, `apt install ffmpeg`, `winget install Gyan.FFmpeg`). The command fails
|
|
133
|
+
rather than skipping: a page referencing a video that was never encoded is broken, not degraded.
|
|
134
|
+
|
|
135
|
+
**Commit the outputs and `.vitops/media-manifest.json`.** Runs are cached on source content plus
|
|
136
|
+
encode settings, so a second run costs a hash per file instead of minutes — but a fresh CI clone
|
|
137
|
+
has neither, and would re-encode everything. Committing both means CI never needs ffmpeg at all.
|
|
138
|
+
It also keeps your history clean: ffmpeg output isn't reproducible across versions, so a CI
|
|
139
|
+
re-encode would rewrite every video on any toolchain bump. Use `--force` when you mean to
|
|
140
|
+
re-encode, and commit the result.
|
|
141
|
+
|
|
142
|
+
Two things worth knowing before you conclude something is broken:
|
|
143
|
+
|
|
144
|
+
- **Frame 0 is often black** on a clip that fades in. That's `--poster-time`, not a bug.
|
|
145
|
+
- **A missing output re-encodes, a corrupt manifest re-encodes everything.** Neither ever reads as
|
|
146
|
+
"already done" — the failure that would cause is silent, and a rebuild would never fix it.
|
|
147
|
+
|
|
96
148
|
## Teaching AI agents (`vitops agents` + `vitops docs`)
|
|
97
149
|
|
|
98
150
|
This package **ships an agent skill** (`skill/SKILL.md`, the [Agent Skills](https://code.claude.com/docs/en/skills)
|