ossclip 0.1.31 → 0.1.34
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 +2 -0
- package/editor-dist/assets/index-Dn813PGp.js +166 -0
- package/editor-dist/index.html +1 -1
- package/package.json +4 -4
- package/src/caption-regen-panel.ts +74 -0
- package/src/caption-report.ts +26 -1
- package/src/edit.ts +1016 -3
- package/src/produce.ts +374 -22
- package/src/program.ts +119 -1
- package/src/publish.ts +696 -0
- package/src/render-replay-args.ts +50 -0
- package/src/replay-argv.ts +16 -0
- package/src/ui/animation.ts +5 -1
- package/editor-dist/assets/index-C537KpNT.js +0 -166
package/editor-dist/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>ossclip editor</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-Dn813PGp.js"></script>
|
|
8
8
|
<link rel="stylesheet" crossorigin href="/assets/index-Bx2VQLP8.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ossclip",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"description": "Local-first CLI video producer: cuts silence and fillers, word-timed captions, face-aware framing, and LLM-planned code-rendered graphics — transcription and rendering never leave your machine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"commander": "^12.1.0",
|
|
37
37
|
"tsx": "^4.19.0",
|
|
38
38
|
"zod": "^3.25.76",
|
|
39
|
-
"@ossclip/core": "0.1.
|
|
40
|
-
"@ossclip/renderer": "0.1.
|
|
41
|
-
"@ossclip/scenes": "0.1.
|
|
39
|
+
"@ossclip/core": "0.1.34",
|
|
40
|
+
"@ossclip/renderer": "0.1.34",
|
|
41
|
+
"@ossclip/scenes": "0.1.34"
|
|
42
42
|
},
|
|
43
43
|
"homepage": "https://github.com/AhsanAyaz/ossclip#readme",
|
|
44
44
|
"bugs": {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { z } from "zod/v4";
|
|
2
|
+
import { defaultProviderName, providerOfLog, type ProviderName } from "@ossclip/core";
|
|
3
|
+
import { lastFlagValue } from "./thumbnail-panel";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Which LLM provider the publish panel's caption regenerate should call
|
|
7
|
+
* (2026-08-29), pure so the usage-log × pin × env matrix is a table test
|
|
8
|
+
* with no server and no filesystem — the thumbnail-panel split. The I/O half
|
|
9
|
+
* lives in edit.ts's /api/publish/regenerate handler, which reads usage.json
|
|
10
|
+
* and command.json and hands them here.
|
|
11
|
+
*
|
|
12
|
+
* Resolution order: the provider usage.json says actually planned this
|
|
13
|
+
* project (`providerOfLog` — truthful even after a §143 fallback, which is
|
|
14
|
+
* why it beats the pin), then the `--llm` pin in command.json's recorded
|
|
15
|
+
* args, then `defaultProviderName`'s detection. Every candidate is PARSED
|
|
16
|
+
* against the provider enum, never coerced: a hand-edited usage.json saying
|
|
17
|
+
* "geminni" falls through to the next rung instead of reaching
|
|
18
|
+
* createProvider as garbage (CLAUDE.md's --source-fit rule).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/** program.ts's `--llm` choices, restated as a schema — the same five names
|
|
22
|
+
* core's ProviderName union spells. */
|
|
23
|
+
const ProviderNameSchema = z.enum(["antigravity", "claude", "claude-cli", "gemini", "mock"]);
|
|
24
|
+
|
|
25
|
+
export type CaptionRegenProviderState =
|
|
26
|
+
| { status: "ready"; provider: ProviderName }
|
|
27
|
+
| { status: "unavailable"; reason: string };
|
|
28
|
+
|
|
29
|
+
export interface CaptionRegenProviderInputs {
|
|
30
|
+
/** usage.json parsed as-is, or null when absent/corrupt — read leniently
|
|
31
|
+
* by the caller (the /api/usage posture). */
|
|
32
|
+
usageLog: unknown;
|
|
33
|
+
/** command.json's recorded args, or null when the workdir has none. */
|
|
34
|
+
commandArgs: readonly string[] | null;
|
|
35
|
+
env: NodeJS.ProcessEnv;
|
|
36
|
+
/** Injected existence check (defaultProviderName's own seam) so the
|
|
37
|
+
* matrix needs no PATH scan. */
|
|
38
|
+
hasBin: (bin: string) => boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function captionRegenProvider(inputs: CaptionRegenProviderInputs): CaptionRegenProviderState {
|
|
42
|
+
// The log's shape is user-editable JSON: pick out the two arrays
|
|
43
|
+
// providerOfLog reads and let anything else read as an empty log.
|
|
44
|
+
const log = (inputs.usageLog ?? {}) as { runs?: unknown; records?: unknown };
|
|
45
|
+
const recorded = providerOfLog({
|
|
46
|
+
runs: Array.isArray(log.runs) ? (log.runs as never[]) : [],
|
|
47
|
+
records: Array.isArray(log.records) ? (log.records as never[]) : [],
|
|
48
|
+
});
|
|
49
|
+
const fromLog = ProviderNameSchema.safeParse(recorded);
|
|
50
|
+
if (fromLog.success) return { status: "ready", provider: fromLog.data };
|
|
51
|
+
const fromPin = ProviderNameSchema.safeParse(
|
|
52
|
+
lastFlagValue(inputs.commandArgs ?? [], ["--llm"]),
|
|
53
|
+
);
|
|
54
|
+
if (fromPin.success) return { status: "ready", provider: fromPin.data };
|
|
55
|
+
// defaultProviderName answers "claude-cli" even with nothing installed —
|
|
56
|
+
// deliberate on the CLI, where the spawn failure carries install guidance.
|
|
57
|
+
// A button in a panel deserves the sentence BEFORE the click, so this
|
|
58
|
+
// restates its four detections as an availability gate and only then takes
|
|
59
|
+
// its pick.
|
|
60
|
+
const reachable =
|
|
61
|
+
inputs.hasBin(inputs.env.OSSCLIP_AGY_BIN ?? "agy") ||
|
|
62
|
+
inputs.hasBin(inputs.env.OSSCLIP_CLAUDE_BIN ?? "claude") ||
|
|
63
|
+
Boolean(inputs.env.GEMINI_API_KEY) ||
|
|
64
|
+
Boolean(inputs.env.ANTHROPIC_API_KEY);
|
|
65
|
+
if (!reachable) {
|
|
66
|
+
return {
|
|
67
|
+
status: "unavailable",
|
|
68
|
+
reason:
|
|
69
|
+
"no LLM provider available — set GEMINI_API_KEY or ANTHROPIC_API_KEY in " +
|
|
70
|
+
"~/.ossclip/.env, or install the claude or agy CLI, then try again",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
return { status: "ready", provider: defaultProviderName(inputs.env, inputs.hasBin) };
|
|
74
|
+
}
|
package/src/caption-report.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
applyCaptionRangeEdits,
|
|
4
4
|
applyCaptionWordHides,
|
|
5
5
|
applyCaptionLineTiming,
|
|
6
|
+
applyCaptionLineWindows,
|
|
6
7
|
captionEditsToKeep,
|
|
7
8
|
isLegacyCaptionKey,
|
|
8
9
|
migrateCaptionKeys,
|
|
@@ -14,6 +15,7 @@ import type {
|
|
|
14
15
|
CaptionKeyMigration,
|
|
15
16
|
CaptionLine,
|
|
16
17
|
OverrideDoc,
|
|
18
|
+
TimeMap,
|
|
17
19
|
} from "@ossclip/core";
|
|
18
20
|
|
|
19
21
|
/**
|
|
@@ -288,10 +290,17 @@ export interface CaptionReconciliation {
|
|
|
288
290
|
* (`produce.ts` parses it at the top of the run). On raw `JSON.parse` output a
|
|
289
291
|
* literal `"__proto__"` key would be assigned THROUGH rather than kept, and
|
|
290
292
|
* the migration would report nothing lost while losing it.
|
|
293
|
+
*
|
|
294
|
+
* `map` is the run's own cutlist map — the one `baseLines` were built through
|
|
295
|
+
* — and exists for the WINDOW layer alone (`applyCaptionLineWindows`): windows
|
|
296
|
+
* are stored in source seconds and these lines speak output seconds. Taken as
|
|
297
|
+
* a parameter rather than rebuilt here for the usual reason: two maps is how
|
|
298
|
+
* this pass and the render would come to place a caption differently.
|
|
291
299
|
*/
|
|
292
300
|
export function reconcileCaptionEdits(
|
|
293
301
|
doc: OverrideDoc,
|
|
294
302
|
baseLines: readonly CaptionLine[],
|
|
303
|
+
map: TimeMap,
|
|
295
304
|
): CaptionReconciliation {
|
|
296
305
|
const log: string[] = [];
|
|
297
306
|
const migration = migrateCaptionKeys(doc.captions, baseLines);
|
|
@@ -351,5 +360,21 @@ export function reconcileCaptionEdits(
|
|
|
351
360
|
const nudged = appliedCaptionEditCount(doc.captionLineTiming, timed.dropped);
|
|
352
361
|
if (nudged > 0) log.push(`▸ ${nudged} caption timing nudge(s) applied`);
|
|
353
362
|
for (const d of timed.dropped) log.push(captionTimingDropLine(d));
|
|
354
|
-
|
|
363
|
+
// WINDOWS last of all — `applyCaptionLayers`' one authoritative order: an
|
|
364
|
+
// absolute window is the user's final answer about when a caption is on
|
|
365
|
+
// screen, so it runs after (and overrides) the nudge layer. Counted with
|
|
366
|
+
// `appliedCaptionEditCount` for the same reason the nudges are: this layer
|
|
367
|
+
// also reports a `duplicate-anchor` drop per EXTRA claimant, so a plain
|
|
368
|
+
// subtraction would erase the line for a window that did apply. No key
|
|
369
|
+
// migration: `captionLineWindows` postdates §137, and the write-back above
|
|
370
|
+
// spreads it through untouched. Drops reuse `captionTimingDropLine` rather
|
|
371
|
+
// than earning a fifth sentence: both records answer "when is this caption
|
|
372
|
+
// on screen", both drop for the SAME two reasons (no line starts there any
|
|
373
|
+
// more / a second claimant), and the fix — re-make it in the timing tool —
|
|
374
|
+
// is one gesture, which is the test that function's docstring sets.
|
|
375
|
+
const windowed = applyCaptionLineWindows(timed.lines, doc.captionLineWindows, map);
|
|
376
|
+
const placed = appliedCaptionEditCount(doc.captionLineWindows, windowed.dropped);
|
|
377
|
+
if (placed > 0) log.push(`▸ ${placed} caption window(s) placed by the editor`);
|
|
378
|
+
for (const d of windowed.dropped) log.push(captionTimingDropLine(d));
|
|
379
|
+
return { doc: migrated, lines: windowed.lines, reanchored: reanchored > 0, log };
|
|
355
380
|
}
|