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.
- package/bin/cli.mjs +25 -4
- package/demo.html +38 -7
- 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
|
-
|
|
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
|
-
|
|
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 === "
|
|
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
|
|
3071
|
-
// blocked
|
|
3072
|
-
// scanning
|
|
3073
|
-
//
|
|
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 out → recoverable (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
|
-
:
|
|
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.
|
|
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": {
|