@qwertybit/pr-preview 0.1.5 → 0.1.7

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 CHANGED
@@ -6,6 +6,25 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.7] — 2026-07-08
10
+
11
+ ### Added
12
+
13
+ - **PR Preview for Teams early-access nudge** — when a recording finishes, a short
14
+ promo points to the early-access list (`pr-preview.com/#teams`). In the manual CLI
15
+ (`pr-preview run`) it prints as the final block with a clickable terminal link
16
+ (OSC 8, degrading to a plain URL where unsupported); in the `/record` skill flow it
17
+ rides along with the `finish_recording` output as a markdown link.
18
+
19
+ ## [0.1.6] — 2026-07-07
20
+
21
+ ### Changed
22
+
23
+ - **Demo shown inline** — the README now embeds the demo directly at the top
24
+ (an inline `<video>` player on GitHub; the autoplaying GIF on npm, which can't
25
+ render video) instead of a separate "Watch in higher quality" link. Demo URLs
26
+ are version-pinned so the CDN/GitHub image proxy never serve a stale clip.
27
+
9
28
  ## [0.1.5] — 2026-07-07
10
29
 
11
30
  ### Changed
@@ -108,7 +127,8 @@ Initial release.
108
127
  CSP `frame-ancestors`) are stripped.
109
128
  - CLI: `pr-preview init`, `pr-preview record`, `pr-preview run`.
110
129
 
111
- [Unreleased]: https://github.com/QwertyBit-Ventures/pr-preview/compare/v0.1.5...HEAD
130
+ [Unreleased]: https://github.com/QwertyBit-Ventures/pr-preview/compare/v0.1.6...HEAD
131
+ [0.1.6]: https://github.com/QwertyBit-Ventures/pr-preview/compare/v0.1.5...v0.1.6
112
132
  [0.1.5]: https://github.com/QwertyBit-Ventures/pr-preview/compare/v0.1.4...v0.1.5
113
133
  [0.1.4]: https://github.com/QwertyBit-Ventures/pr-preview/compare/v0.1.0...v0.1.4
114
134
  [0.1.0]: https://github.com/QwertyBit-Ventures/pr-preview/releases/tag/v0.1.0
package/README.md CHANGED
@@ -15,12 +15,13 @@
15
15
  <a href="https://pr-preview.com"><img src="https://img.shields.io/badge/website-pr--preview.com-635bff" alt="pr-preview.com" /></a>
16
16
  </p>
17
17
 
18
+ <!-- GitHub renders this <video> as an inline player; npm strips it and falls back to the GIF below. -->
18
19
  <p align="center">
19
- <img src="https://cdn.jsdelivr.net/npm/@qwertybit/pr-preview/assets/demo.gif" alt="PR Preview demo a Claude Code prompt drives the app in Chrome and produces before/after videos" width="720" />
20
+ <video src="https://cdn.jsdelivr.net/npm/@qwertybit/pr-preview@0.1.6/assets/demo.mp4" width="720" controls autoplay muted loop playsinline></video>
20
21
  </p>
21
22
 
22
23
  <p align="center">
23
- <a href="https://cdn.jsdelivr.net/npm/@qwertybit/pr-preview/assets/demo.mp4">▶ Watch in higher quality (MP4)</a>
24
+ <img src="https://cdn.jsdelivr.net/npm/@qwertybit/pr-preview@0.1.6/assets/demo.gif" alt="PR Preview demo — a Claude Code prompt drives the app in Chrome and produces before/after videos" width="720" />
24
25
  </p>
25
26
 
26
27
  **PR Preview** opens your app in a controlled Chrome window and records the journey you perform —
package/dist/cli/index.js CHANGED
@@ -2560,6 +2560,31 @@ async function detectLocalApps(preferred) {
2560
2560
  return results.filter((r) => r !== null);
2561
2561
  }
2562
2562
 
2563
+ // src/branding.ts
2564
+ import pc2 from "picocolors";
2565
+ var TEAMS_URL = "https://pr-preview.com/#teams";
2566
+ function osc8(url, label) {
2567
+ return `\x1B]8;;${url}\x1B\\${label}\x1B]8;;\x1B\\`;
2568
+ }
2569
+ function isInteractive() {
2570
+ return Boolean(process.stdout.isTTY) && process.env.TERM !== "dumb";
2571
+ }
2572
+ function teamsPromoTerminal() {
2573
+ const link = isInteractive() ? osc8(TEAMS_URL, TEAMS_URL) : TEAMS_URL;
2574
+ return [
2575
+ `${pc2.magenta("\u2605")} ${pc2.bold("Love it? PR Preview for Teams is coming")} \u2014`,
2576
+ pc2.dim(" hosted clips, team reviews & sharing."),
2577
+ ` Join the early-access list \u2192 ${pc2.cyan(link)}`
2578
+ ].join("\n");
2579
+ }
2580
+ function teamsPromoMarkdown() {
2581
+ return `\u{1F49C} Enjoying this? **PR Preview for Teams** \u2014 hosted clips, team reviews & sharing \u2014 is coming soon. [Join the early-access list \u2192](${TEAMS_URL})`;
2582
+ }
2583
+ function printTeamsPromo() {
2584
+ console.log();
2585
+ console.log(teamsPromoTerminal());
2586
+ }
2587
+
2563
2588
  // src/mcp/server.ts
2564
2589
  function urlAsk(found, hasConfig) {
2565
2590
  const lines = ["No URL was specified \u2014 I won't guess which app to record."];
@@ -2711,7 +2736,9 @@ ${snapshot}${SNAPSHOT_HINT}`);
2711
2736
  `Recording complete. Output:
2712
2737
  ${rel.map((r) => ` - ${r}`).join("\n")}${note}
2713
2738
 
2714
- Drag the file into your PR description to embed it, or commit it and open the PR.`
2739
+ Drag the file into your PR description to embed it, or commit it and open the PR.
2740
+
2741
+ ` + teamsPromoMarkdown()
2715
2742
  );
2716
2743
  } catch (e) {
2717
2744
  return fail(e);
@@ -2777,7 +2804,7 @@ async function mcpCommand(repoRoot) {
2777
2804
 
2778
2805
  // src/cli/commands/record.ts
2779
2806
  import path16 from "path";
2780
- import pc2 from "picocolors";
2807
+ import pc3 from "picocolors";
2781
2808
 
2782
2809
  // src/recorder/journey.ts
2783
2810
  import { readFile as readFile5, writeFile as writeFile4, mkdir as mkdir5 } from "fs/promises";
@@ -2831,7 +2858,7 @@ async function recordCommand(repoRoot, opts) {
2831
2858
  const boot = await bootstrap(repoRoot, config, "record");
2832
2859
  await boot.startApp("after", repoRoot);
2833
2860
  await boot.openBrowser("after");
2834
- log.info(`Harness open at ${pc2.bold(boot.harness.url)}`);
2861
+ log.info(`Harness open at ${pc3.bold(boot.harness.url)}`);
2835
2862
  log.step("Click Record in the sidebar, perform the journey, then Confirm.");
2836
2863
  const steps = await boot.session.recordUntilConfirmed();
2837
2864
  if (steps.length === 0) {
@@ -2854,7 +2881,7 @@ async function recordCommand(repoRoot, opts) {
2854
2881
  // src/cli/commands/run.ts
2855
2882
  import { existsSync as existsSync10 } from "fs";
2856
2883
  import path17 from "path";
2857
- import pc3 from "picocolors";
2884
+ import pc4 from "picocolors";
2858
2885
 
2859
2886
  // src/cli/util/openPath.ts
2860
2887
  import { spawn as spawn4 } from "child_process";
@@ -2937,7 +2964,7 @@ async function runCommand(repoRoot, opts) {
2937
2964
  );
2938
2965
  }
2939
2966
  const base = await detectBase(repoRoot, opts.base ?? config.baseBranch);
2940
- log.info(`PR base: ${pc3.bold(base.ref)} (${base.sha.slice(0, 10)}, via ${base.source})`);
2967
+ log.info(`PR base: ${pc4.bold(base.ref)} (${base.sha.slice(0, 10)}, via ${base.source})`);
2941
2968
  if (await tryGit(repoRoot, ["status", "--porcelain"])) {
2942
2969
  log.info("Heads up: the AFTER clip captures your working tree, including uncommitted changes.");
2943
2970
  }
@@ -2964,7 +2991,7 @@ async function runCommand(repoRoot, opts) {
2964
2991
  boot.session.setBranches(base.ref, currentBranch);
2965
2992
  const beforeServer = await boot.startApp("before", worktree.dir);
2966
2993
  await boot.openBrowser("before");
2967
- log.info(`Harness open at ${pc3.bold(boot.harness.url)}`);
2994
+ log.info(`Harness open at ${pc4.bold(boot.harness.url)}`);
2968
2995
  log.step(`Record the journey on BEFORE (${base.ref}), then Confirm in the sidebar.`);
2969
2996
  await boot.session.promptResetChoice();
2970
2997
  const beforeSteps = await boot.session.recordUntilConfirmed();
@@ -3011,11 +3038,12 @@ async function runCommand(repoRoot, opts) {
3011
3038
  outputs: { before: before.paths[0], after: after.paths[0] }
3012
3039
  });
3013
3040
  console.log();
3014
- log.success(pc3.bold("Done \u2014 drag these into your PR description:"));
3041
+ log.success(pc4.bold("Done \u2014 drag these into your PR description:"));
3015
3042
  for (const p of [...before.paths, ...after.paths]) {
3016
3043
  log.step(path17.relative(repoRoot, p));
3017
3044
  }
3018
3045
  revealFiles([...before.paths, ...after.paths], outDir);
3046
+ printTeamsPromo();
3019
3047
  } finally {
3020
3048
  await runCleanups();
3021
3049
  }
@@ -3027,12 +3055,12 @@ async function runSingleClip(repoRoot, config, outDir, opts) {
3027
3055
  const branch = await branchName(repoRoot, "app");
3028
3056
  let server;
3029
3057
  if (opts.url) {
3030
- log.info(`Using your running app at ${pc3.bold(opts.url)} (single clip).`);
3058
+ log.info(`Using your running app at ${pc4.bold(opts.url)} (single clip).`);
3031
3059
  } else {
3032
3060
  server = await boot.startApp("before", repoRoot);
3033
3061
  }
3034
3062
  await boot.openBrowser("before");
3035
- log.info(`Harness open at ${pc3.bold(boot.harness.url)}`);
3063
+ log.info(`Harness open at ${pc4.bold(boot.harness.url)}`);
3036
3064
  log.step(`Record the journey, then Confirm to save the clip.`);
3037
3065
  await boot.session.promptResetChoice();
3038
3066
  const steps = await boot.session.recordUntilConfirmed();
@@ -3053,22 +3081,23 @@ async function runSingleClip(repoRoot, config, outDir, opts) {
3053
3081
  boot.session.setPhase("done");
3054
3082
  boot.harness.bus.send({ type: "DONE", outputs: { before: clip.paths[0] } });
3055
3083
  console.log();
3056
- log.success(pc3.bold("Done \u2014 your clip:"));
3084
+ log.success(pc4.bold("Done \u2014 your clip:"));
3057
3085
  for (const p of clip.paths) log.step(path17.relative(repoRoot, p));
3058
3086
  revealFiles(clip.paths, outDir);
3087
+ printTeamsPromo();
3059
3088
  } finally {
3060
3089
  await runCleanups();
3061
3090
  }
3062
3091
  }
3063
3092
  async function runWithExternalApp(repoRoot, config, outDir, opts) {
3064
- log.info(`Using your running app at ${pc3.bold(opts.url)} (no dev server managed).`);
3093
+ log.info(`Using your running app at ${pc4.bold(opts.url)} (no dev server managed).`);
3065
3094
  try {
3066
3095
  const boot = await bootstrap(repoRoot, config, "run", { fixedUrl: opts.url });
3067
3096
  boot.session.setPasses(2);
3068
3097
  const beforeBranch = await branchName(repoRoot, "before");
3069
3098
  boot.session.setBranches(beforeBranch, "your PR branch");
3070
3099
  await boot.openBrowser("before");
3071
- log.info(`Harness open at ${pc3.bold(boot.harness.url)}`);
3100
+ log.info(`Harness open at ${pc4.bold(boot.harness.url)}`);
3072
3101
  log.step(`Record the journey on your app (currently ${beforeBranch}), then Confirm.`);
3073
3102
  await boot.session.promptResetChoice();
3074
3103
  const beforeSteps = await boot.session.recordUntilConfirmed();
@@ -3084,7 +3113,7 @@ async function runWithExternalApp(repoRoot, config, outDir, opts) {
3084
3113
  { branch: beforeBranch, baseBranch: beforeBranch, timestamp }
3085
3114
  );
3086
3115
  log.success(`${before.paths.map((p) => path17.basename(p)).join(" + ")} (${before.frameCount} frames)`);
3087
- log.info(pc3.bold("\u2192 Switch your app to the PR branch and restart it on the same URL, then Continue in the harness."));
3116
+ log.info(pc4.bold("\u2192 Switch your app to the PR branch and restart it on the same URL, then Continue in the harness."));
3088
3117
  boot.harness.bus.send({
3089
3118
  type: "MANUAL_PAUSE",
3090
3119
  stepId: null,
@@ -3113,9 +3142,10 @@ async function runWithExternalApp(repoRoot, config, outDir, opts) {
3113
3142
  boot.session.setPhase("done");
3114
3143
  boot.harness.bus.send({ type: "DONE", outputs: { before: before.paths[0], after: after.paths[0] } });
3115
3144
  console.log();
3116
- log.success(pc3.bold("Done \u2014 drag these into your PR description:"));
3145
+ log.success(pc4.bold("Done \u2014 drag these into your PR description:"));
3117
3146
  for (const p of [...before.paths, ...after.paths]) log.step(path17.relative(repoRoot, p));
3118
3147
  revealFiles([...before.paths, ...after.paths], outDir);
3148
+ printTeamsPromo();
3119
3149
  } finally {
3120
3150
  await runCleanups();
3121
3151
  }