transitions-refine 0.3.5 → 0.3.6

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 (3) hide show
  1. package/bin/cli.mjs +25 -4
  2. package/demo.html +38 -7
  3. package/package.json +1 -1
package/bin/cli.mjs CHANGED
@@ -24,6 +24,13 @@ import { homedir } from "node:os";
24
24
  const PKG_ROOT = fileURLToPath(new URL("..", import.meta.url));
25
25
  const CWD = process.cwd();
26
26
  const HOME = process.env.HOME || homedir();
27
+ const PKG_VERSION = (() => {
28
+ try {
29
+ return JSON.parse(readFileSync(join(PKG_ROOT, "package.json"), "utf8")).version || "0";
30
+ } catch {
31
+ return "0";
32
+ }
33
+ })();
27
34
 
28
35
  const MARK_START = "<!-- timeline-inject:start -->";
29
36
  const MARK_END = "<!-- timeline-inject:end -->";
@@ -81,14 +88,27 @@ function escapeRe(s) {
81
88
 
82
89
  // Copy a whole skill directory from the package into the user's project so the
83
90
  // in-IDE agent (/refine live) and any spawned cursor-agent can read it.
91
+ //
92
+ // Crucially this REFRESHES a stale copy on upgrade. An older installed skill can
93
+ // shadow newer job handling — e.g. a pre-scan `refine-live` skill that doesn't
94
+ // know how to answer kind:"scan" jobs, so scan jobs time out and the panel hangs
95
+ // on "Agent is scanning…". We stamp the package version into the skill dir and
96
+ // re-copy whenever it's missing or mismatched (so we don't clobber every run).
84
97
  function dropSkill(name) {
85
98
  const src = join(PKG_ROOT, ".agents/skills", name);
86
99
  const destDir = join(CWD, ".agents/skills", name);
87
100
  if (!existsSync(src)) return false;
88
- if (existsSync(destDir)) return "exists";
101
+ const marker = join(destDir, ".refine-version");
102
+ const existed = existsSync(destDir);
103
+ if (existed) {
104
+ let installed = null;
105
+ try { installed = readFileSync(marker, "utf8").trim(); } catch {}
106
+ if (installed === PKG_VERSION) return "exists";
107
+ }
89
108
  mkdirSync(dirname(destDir), { recursive: true });
90
- cpSync(src, destDir, { recursive: true });
91
- return true;
109
+ cpSync(src, destDir, { recursive: true, force: true });
110
+ try { writeFileSync(marker, PKG_VERSION + "\n"); } catch {}
111
+ return existed ? "updated" : true;
92
112
  }
93
113
 
94
114
  // ── agent CLI (for the persistent LLM path) ──────────────────────────────────
@@ -162,7 +182,8 @@ function cmdLive(args) {
162
182
  for (const name of ["refine-live", "transitions-dev"]) {
163
183
  const r = dropSkill(name);
164
184
  if (r === true) log(`✓ added .agents/skills/${name}`);
165
- else if (r === "exists") log(`✓ ${name} skill already present`);
185
+ else if (r === "updated") log(`✓ updated .agents/skills/${name} (now v${PKG_VERSION})`);
186
+ else if (r === "exists") log(`✓ ${name} skill already present (v${PKG_VERSION})`);
166
187
  }
167
188
 
168
189
  // 2.5) ensure an agent CLI so the relay can answer LLM jobs itself — this is
package/demo.html CHANGED
@@ -1085,8 +1085,20 @@
1085
1085
  .tl-gate-beam-fill { display: block; width: 100%; height: 100%; border-radius: 36px; background: #fff; }
1086
1086
  .tl-gate-sub { margin: 16px 0 0; max-width: 244px; font-size: 12px; font-weight: 400; line-height: 14px;
1087
1087
  color: #6f6f6f; text-wrap: pretty; }
1088
+ /* recovery actions when a scan errored/timed out — never trap the panel */
1089
+ .tl-gate-actions { display: flex; align-items: center; gap: 8px; margin: 18px 0 0; }
1090
+ .tl-gate-btn { height: 32px; padding: 0 14px; border: 0; border-radius: 60px; cursor: pointer;
1091
+ font: 500 12px/14px inherit; color: #2c2c2c; background: #f3f3f3; white-space: nowrap;
1092
+ box-shadow: 0 1px 3px 0 rgba(0,0,0,0.04), inset 0 0 0 1px rgba(0,0,0,0.06), inset 0 -1px 0 0 rgba(0,0,0,0.10);
1093
+ transition: background 140ms cubic-bezier(0.4,0,0.2,1); }
1094
+ .tl-gate-btn:hover { background: #ececec; }
1095
+ .tl-gate-btn:active { background: #e6e6e6; }
1096
+ .tl-gate-btn-primary { color: #fff; background: #0a84ff;
1097
+ box-shadow: 0 0 0 1px rgba(0,101,208,0.10) inset, 0 -1px 0 0 rgba(3,66,142,0.15) inset, 0 1px 3px 0 rgba(4,41,117,0.08); }
1098
+ .tl-gate-btn-primary:hover { background: #0a78e6; }
1088
1099
  @media (prefers-reduced-motion: reduce) {
1089
- .tl-gate, .tl-gate-pill-wrap .tl-gate-beam { animation: none !important; } }
1100
+ .tl-gate, .tl-gate-pill-wrap .tl-gate-beam { animation: none !important; }
1101
+ .tl-gate-btn { transition: none !important; } }
1090
1102
  /* dot-matrix loader — ported from @dotmatrix/dotm-square-14
1091
1103
  (github.com/zzzzshawn/matrix, MIT). A 5×5 dot grid cross-fades through four
1092
1104
  frame masks in the sequence 0→1→2→3→2→1, dot opacities x=1 / o=0.52 / .=0.08.
@@ -2718,6 +2730,9 @@
2718
2730
  const[acceptError,setAcceptError]=useState(null);
2719
2731
  // ── grouped scan (agent reads source → Open/Close phases) ──
2720
2732
  const[groupScanState,setGroupScanState]=useState("idle"); // idle | scanning | done | error
2733
+ // Escape hatch for the gate: if a scan errors/times out the user can choose
2734
+ // to proceed with the flat (ungrouped) list instead of being trapped.
2735
+ const[skipGrouping,setSkipGrouping]=useState(false);
2721
2736
  const didGroupScanRef=useRef(false);
2722
2737
  const[refineLabel,setRefineLabel]=useState(null);
2723
2738
  const[appliedIds,setAppliedIds]=useState({});
@@ -3010,6 +3025,7 @@
3010
3025
  const rescanTransitions=useCallback(()=>{
3011
3026
  try{localStorage.removeItem(GROUP_STORE_KEY);}catch{}
3012
3027
  registry.clearGroups();
3028
+ setSkipGrouping(false); // re-engage the gate so a fresh scan can be shown
3013
3029
  runGroupScan();
3014
3030
  },[runGroupScan,GROUP_STORE_KEY,registry]);
3015
3031
  // Gate the auto group-scan behind a live agent: the panel stays on the
@@ -3067,13 +3083,19 @@
3067
3083
 
3068
3084
  // gate: the panel is unusable until a live agent is connected AND it has
3069
3085
  // scanned the page's transitions.
3070
- // loading → first /health probe pending (blank, avoids a text flash)
3071
- // blocked → no live agent → "Before we start" (run /refine live)
3072
- // scanning → live agent, scan in flight → "Agent is scanning…"
3073
- // ready live + scan done the real timeline UI
3086
+ // loading → first /health probe pending (blank, avoids a text flash)
3087
+ // blocked → no live agent → "Before we start" (run /refine live)
3088
+ // scanning → live agent, scan in flight → "Agent is scanning…"
3089
+ // scan-error scan failed/timed outrecoverable (retry / continue)
3090
+ // ready → live + scan settled (done or idle, or user skipped) → real UI
3091
+ // NOTE: "idle" and "error" must NOT keep us on the scanning screen, or a
3092
+ // timed-out scan (e.g. an older /refine-live skill that can't answer scan
3093
+ // jobs) would trap the panel forever with no way out.
3074
3094
  const gate = !live
3075
3095
  ? (llmAvailable===null ? "loading" : "blocked")
3076
- : (groupScanState==="done" ? "ready" : "scanning");
3096
+ : groupScanState==="scanning" ? "scanning"
3097
+ : (groupScanState==="error" && !skipGrouping) ? "scan-error"
3098
+ : "ready";
3077
3099
  return h(React.Fragment,null,
3078
3100
  render&&h("div",{className:"t-panel-slide","data-timeline-panel":true,
3079
3101
  "data-open":panelOpen?"true":"false","data-phase":phase,style:{height:panelHeight+"px"}},
@@ -3109,7 +3131,16 @@
3109
3131
  h("span",{className:"tl-gate-pill"},
3110
3132
  h(DotmLoader),
3111
3133
  h("span",{className:"tl-gate-pill-label"},"Agent is scanning your transitions"))),
3112
- h("p",{className:"tl-gate-sub"},"Just a moment while we get things ready.")))),
3134
+ h("p",{className:"tl-gate-sub"},"Just a moment while we get things ready."))),
3135
+ gate==="scan-error" && h("div",{className:"tl-gate"},
3136
+ h("div",{className:"tl-gate-col"},
3137
+ h("div",{className:"tl-gate-title"},"We couldn’t scan your transitions"),
3138
+ h("p",{className:"tl-gate-text"},
3139
+ "The agent didn’t finish grouping. Make sure ",h("code",{className:"tl-code"},"/refine live"),
3140
+ " is running with the latest skill, then try again — or continue with the ungrouped list."),
3141
+ h("div",{className:"tl-gate-actions"},
3142
+ h("button",{className:"tl-gate-btn tl-gate-btn-primary",onClick:rescanTransitions},"Try again"),
3143
+ h("button",{className:"tl-gate-btn",onClick:()=>setSkipGrouping(true)},"Continue without grouping"))))),
3113
3144
  toast&&createPortal(
3114
3145
  h("div",{className:"tl-toast-wrap","aria-live":"polite"},
3115
3146
  h("div",{className:cx("tl-toast",toast.closing&&"is-closing")},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "transitions-refine",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "Live, agent-driven Refine panel for CSS/Motion transitions — injects a timeline + Refine UI and runs transitions.dev suggestions via your coding agent.",
5
5
  "type": "module",
6
6
  "bin": {