slidemind 0.3.13 → 0.3.14

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.
Files changed (49) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/AGENTS.md +86 -21
  4. package/README.md +14 -17
  5. package/bin/slidemind.mjs +65 -4969
  6. package/package.json +1 -1
  7. package/skills/create-slides/SKILL.md +113 -74
  8. package/skills/search/SKILL.md +25 -7
  9. package/src/bridge/build-and-push.mjs +108 -15
  10. package/src/bridge/bundle.mjs +69 -0
  11. package/src/bridge/capture.mjs +19 -0
  12. package/src/bridge/client/brands.mjs +622 -0
  13. package/src/bridge/client/engine-routing.mjs +237 -0
  14. package/src/bridge/client/engine-search.mjs +310 -0
  15. package/src/bridge/client/engine-slides.mjs +435 -0
  16. package/src/bridge/client/pane-ops.mjs +335 -0
  17. package/src/bridge/client/presentations.mjs +587 -0
  18. package/src/bridge/client/render.mjs +317 -0
  19. package/src/bridge/client/transport.mjs +695 -0
  20. package/src/bridge/client.mjs +80 -3452
  21. package/src/bridge/config.mjs +1 -1
  22. package/src/bridge/engine-download.mjs +15 -2
  23. package/src/builders/build.mjs +11 -804
  24. package/src/builders/draft-check.mjs +376 -0
  25. package/src/builders/lint.mjs +38 -2
  26. package/src/builders/preview.html +799 -0
  27. package/src/builders/review.mjs +23 -4
  28. package/src/builders/visual-families.mjs +6 -8
  29. package/src/cli/common.mjs +179 -0
  30. package/src/cli/concepts.mjs +310 -0
  31. package/src/cli/connection.mjs +147 -0
  32. package/src/cli/deck.mjs +608 -0
  33. package/src/cli/designs.mjs +113 -0
  34. package/src/cli/diagnose.mjs +141 -0
  35. package/src/cli/download-bundle.mjs +144 -0
  36. package/src/cli/engine-helpers.mjs +429 -0
  37. package/src/cli/engine-results.mjs +320 -0
  38. package/src/cli/help.mjs +201 -0
  39. package/src/cli/install.mjs +257 -0
  40. package/src/cli/read.mjs +120 -0
  41. package/src/cli/ready.mjs +805 -0
  42. package/src/cli/search.mjs +608 -0
  43. package/src/cli/session.mjs +177 -0
  44. package/src/cli/setup.mjs +282 -0
  45. package/src/cli/slide-reuse.mjs +334 -0
  46. package/src/cli/status.mjs +118 -0
  47. package/src/cli/workspace.mjs +302 -0
  48. package/src/installers/codex.mjs +1 -1
  49. package/src/update-check.mjs +231 -0
@@ -50,6 +50,7 @@ import {
50
50
  getDesignBundle,
51
51
  getDesignBundleForBrand,
52
52
  getDesignBundleForBrandAnyBackend,
53
+ listBrands,
53
54
  } from "./client.mjs";
54
55
  import { updateCliInstruction } from "../install-channel.mjs";
55
56
 
@@ -655,6 +656,74 @@ export function readDeckBundleRef(deckDir) {
655
656
  // Resolve the materialized bundle directory a deck should read its design files
656
657
  // from (via the deck's .bundle-ref.json). Returns null when no bundle has been
657
658
  // materialized for the deck's CD yet.
659
+ // Re-sync a bound deck's design bundle to the design as it stands right now.
660
+ //
661
+ // `ready` materializes at session start, but a corporate design can change
662
+ // while a session runs, and every local step afterwards (HTML build, lint,
663
+ // capture, the draft checks, and the etag the server render is validated
664
+ // against) reads this cached copy. So the deck commands call this first: the
665
+ // sync is etag-gated and incremental, so an unchanged design downloads nothing.
666
+ //
667
+ // Returns { result, changed } — `changed` is true when the design's etag moved,
668
+ // in which case the deck's ref is rewritten to the new one. Returns null when
669
+ // the deck is unbound or the backend cannot be reached: a stale-but-present
670
+ // bundle is what keeps the host-free steps host-free.
671
+ export async function refreshDeckBundle(deckDir) {
672
+ if (!deckDir) return null;
673
+ const ref = readDeckBundleRef(deckDir);
674
+ if (!ref?.presFP) return null; // unbound — `ready <deck>` binds it
675
+ // A refresh must never make a host-free step wait like a render does: a dead
676
+ // backend has to fall back to the cached bundle in seconds, not the 35s
677
+ // default plus retries. A negative delay list filters to empty, which also
678
+ // turns the transient retry off — the next command retries anyway.
679
+ const prev = {
680
+ SLIDEMIND_BRIDGE_TIMEOUT_MS: process.env.SLIDEMIND_BRIDGE_TIMEOUT_MS,
681
+ SLIDEMIND_BRIDGE_RETRY_DELAYS_MS: process.env.SLIDEMIND_BRIDGE_RETRY_DELAYS_MS,
682
+ };
683
+ process.env.SLIDEMIND_BRIDGE_TIMEOUT_MS ??=
684
+ process.env.SLIDEMIND_BUNDLE_REFRESH_TIMEOUT_MS || "6000";
685
+ process.env.SLIDEMIND_BRIDGE_RETRY_DELAYS_MS ??= "-1";
686
+ try {
687
+ const result = await materializeBundle(null, { brandId: ref.presFP });
688
+ if (!result) return null;
689
+ const changed = Boolean(result.etag) && result.etag !== ref.etag;
690
+ if (changed) {
691
+ writeDeckBundleRef(deckDir, { presFP: result.presFP, etag: result.etag });
692
+ }
693
+ return { result, changed };
694
+ } catch {
695
+ return null; // offline / unconnected / design withdrawn — keep the cache
696
+ } finally {
697
+ for (const [k, v] of Object.entries(prev)) {
698
+ if (v === undefined) delete process.env[k];
699
+ else process.env[k] = v;
700
+ }
701
+ }
702
+ }
703
+
704
+ // The design an UNBOUND deck can be rendered/previewed against WITHOUT a
705
+ // PowerPoint window — the same ladder `ready` walks once no window is in play:
706
+ // the deck's own binding → the one design cached on this machine → the one
707
+ // design the connected backends list. Returns the presFP, or null when nothing
708
+ // answers unambiguously (0 or several of either; no brand route) — the caller
709
+ // then falls back to the window registry, which is the only remaining source
710
+ // of a design (an open presentation's active corporate design) and the one
711
+ // place that legitimately needs the pane.
712
+ export async function resolveWindowlessBrand(deckDir) {
713
+ const bound = readDeckBundleRef(deckDir)?.presFP;
714
+ if (bound) return bound;
715
+ const cached = listCachedBundles();
716
+ if (cached.length === 1) return cached[0].presFP;
717
+ if (cached.length > 1) return null;
718
+ let brands = [];
719
+ try {
720
+ brands = await listBrands();
721
+ } catch {
722
+ brands = [];
723
+ }
724
+ return brands.length === 1 ? brands[0].presFP : null;
725
+ }
726
+
658
727
  export function resolveBundleDirForDeck(deckDir) {
659
728
  const ref = readDeckBundleRef(deckDir);
660
729
  if (!ref?.presFP) return null;
@@ -276,6 +276,25 @@ export async function loadConnectorPreview(bundleDir) {
276
276
  return { globalName: String(preview.globalName || "SlidemindConnectorDerive"), source: preview.source };
277
277
  }
278
278
 
279
+ /**
280
+ * The measured draft checks (`draftChecks: { fill, typeScale, typeScaleTokens,
281
+ * overlap }`) exported from the same artifact — the rules the add-in's own
282
+ * see_draft runs. view-draft calls them on its local capture so both harnesses
283
+ * teach one contract. The CLI ships no draft-check rule of its own; null when
284
+ * the bundle has no engine or the engine predates the export.
285
+ */
286
+ export async function loadDraftChecks(bundleDir) {
287
+ if (!bundleDir || !bundleHasCaptureEngine(bundleDir)) return null;
288
+ const enginePath = path.join(bundleDir, "capture-engine.mjs");
289
+ const version = readBundleMeta(bundleDir).captureEngineVersion ?? "0";
290
+ const engine = await import(
291
+ `${pathToFileURL(enginePath).href}?v=${encodeURIComponent(version)}`
292
+ );
293
+ const checks = engine.draftChecks;
294
+ if (!checks || typeof checks.fill !== "function") return null;
295
+ return checks;
296
+ }
297
+
279
298
  export async function loadChartPreview(bundleDir) {
280
299
  if (!bundleDir || !bundleHasCaptureEngine(bundleDir)) return null;
281
300
  const enginePath = path.join(bundleDir, "capture-engine.mjs");