@limno/core 0.1.11 → 0.1.13

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/dist/index.cjs CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
+ FRAME_FOCUS_GUARD: () => FRAME_FOCUS_GUARD,
33
34
  ROUTE: () => ROUTE,
34
35
  SOURCE_FILES: () => SOURCE_FILES,
35
36
  TEMPLATE_FILES: () => TEMPLATE_FILES,
@@ -297,6 +298,11 @@ async function uncommitted(root, files) {
297
298
  const top = (await git(root, ["rev-parse", "--show-toplevel"])).out.trim();
298
299
  return files.filter((f) => dirty.has(top ? import_node_path2.default.relative(top, import_node_path2.default.join(root, f)) : f) || dirty.has(f));
299
300
  }
301
+ async function committedSince(root, files, since) {
302
+ if (!files.length) return false;
303
+ const r = await git(root, ["log", `--since=${Math.floor(since / 1e3)}`, "--format=%h", "--", ...files]);
304
+ return r.code === 0 && r.out.trim().length > 0;
305
+ }
300
306
  async function commitFiles(root, files, message) {
301
307
  if (!await isGitRepo(root)) return { ok: false, error: "This project isn\u2019t a git repository." };
302
308
  for (const marker of ["MERGE_HEAD", "rebase-merge", "rebase-apply", "CHERRY_PICK_HEAD"]) {
@@ -708,7 +714,9 @@ function createRunner(root, folder, onState) {
708
714
  return {
709
715
  async info(force) {
710
716
  if (state.commit && !state.commit.committed && state.commit.uncommitted.length && !child) {
711
- state.commit = { ...state.commit, uncommitted: await uncommitted(root, state.files) };
717
+ const left = await uncommitted(root, state.files);
718
+ const undone = !left.length && !await committedSince(root, state.files, state.startedAt ?? 0);
719
+ state.commit = { ...state.commit, uncommitted: left, undone };
712
720
  }
713
721
  return { agents: await detectAgents(force), allowed: allowed(), autoCommit: autoCommitOn(), state };
714
722
  },
@@ -967,6 +975,7 @@ data: ${JSON.stringify(data)}
967
975
  let watcher = null;
968
976
  let watchedIno = -1;
969
977
  let debounce;
978
+ const pending = /* @__PURE__ */ new Set();
970
979
  function startWatching() {
971
980
  watcher?.close();
972
981
  watcher = null;
@@ -974,10 +983,13 @@ data: ${JSON.stringify(data)}
974
983
  watchedIno = (0, import_node_fs4.statSync)(folder).ino;
975
984
  watcher = (0, import_node_fs4.watch)(folder, (_event, name) => {
976
985
  if (!name || name.endsWith(".tmp")) return;
986
+ pending.add(String(name));
977
987
  clearTimeout(debounce);
978
988
  debounce = setTimeout(() => {
979
- if (name === "changeset.json") onChangeSetFile();
980
- if (name === "agent.json") pushAgent();
989
+ const names = new Set(pending);
990
+ pending.clear();
991
+ if (names.has("changeset.json")) onChangeSetFile();
992
+ if (names.has("agent.json")) pushAgent();
981
993
  }, 40);
982
994
  });
983
995
  watcher.unref?.();
@@ -1214,8 +1226,12 @@ var SOURCE_FILES = /\.(jsx|tsx|js|mjs)$/;
1214
1226
  function shouldStamp(file, include = SOURCE_FILES) {
1215
1227
  return include.test(file) && !/[\\/]node_modules[\\/]/.test(file) && !file.startsWith("\0");
1216
1228
  }
1229
+
1230
+ // src/frameGuard.ts
1231
+ var FRAME_FOCUS_GUARD = `(function(){if(window.name!=="limno-frame"||window.__limnoGuard)return;window.__limnoGuard=true;var g=function(e){if(!window.__limnoHoldFocus||e.relatedTarget)return;e.stopImmediatePropagation();};window.addEventListener("blur",g,true);window.addEventListener("focusout",g,true);document.addEventListener("visibilitychange",function(e){if(window.__limnoHoldFocus)e.stopImmediatePropagation();},true);})();`;
1217
1232
  // Annotate the CommonJS export names for ESM import in node:
1218
1233
  0 && (module.exports = {
1234
+ FRAME_FOCUS_GUARD,
1219
1235
  ROUTE,
1220
1236
  SOURCE_FILES,
1221
1237
  TEMPLATE_FILES,
package/dist/index.d.cts CHANGED
@@ -117,6 +117,8 @@ interface RunCommit {
117
117
  };
118
118
  /** Why an auto-commit didn't happen. */
119
119
  note?: string;
120
+ /** No longer changed and never committed: the edits were undone (reverted or discarded). */
121
+ undone?: boolean;
120
122
  }
121
123
  interface RunnerInfo {
122
124
  agents: RunnerAgent[];
@@ -134,4 +136,14 @@ interface RunnerInfo {
134
136
  */
135
137
  declare function isLocalRequest(req: IncomingMessage): boolean;
136
138
 
137
- export { type AgentStatus, type LimnoServer, type LimnoService, type LimnoServiceOptions, ROUTE, type RunnerAgent, type RunnerAgentId, type RunnerInfo, type RunnerLogEntry, type RunnerState, SOURCE_FILES, TEMPLATE_FILES, createLimnoService, isLocalRequest, overlayFile, shouldStamp, stampSource, stampTemplate, startServer };
139
+ /**
140
+ * A tiny inline script for the top of the page, ahead of the app's own code. Inside Limno's
141
+ * edit frame only, while the overlay is selecting or commenting (`__limnoHoldFocus`), it keeps
142
+ * "the page lost focus" from the app: an open menu, popover or select then stays open while it's
143
+ * edited, and through tab or app switches. Window listeners run in the order they were added, so
144
+ * this has to be registered before the app's; the overlay installs the same guard later as a
145
+ * fallback (and skips it when this one is there).
146
+ */
147
+ declare const FRAME_FOCUS_GUARD = "(function(){if(window.name!==\"limno-frame\"||window.__limnoGuard)return;window.__limnoGuard=true;var g=function(e){if(!window.__limnoHoldFocus||e.relatedTarget)return;e.stopImmediatePropagation();};window.addEventListener(\"blur\",g,true);window.addEventListener(\"focusout\",g,true);document.addEventListener(\"visibilitychange\",function(e){if(window.__limnoHoldFocus)e.stopImmediatePropagation();},true);})();";
148
+
149
+ export { type AgentStatus, FRAME_FOCUS_GUARD, type LimnoServer, type LimnoService, type LimnoServiceOptions, ROUTE, type RunnerAgent, type RunnerAgentId, type RunnerInfo, type RunnerLogEntry, type RunnerState, SOURCE_FILES, TEMPLATE_FILES, createLimnoService, isLocalRequest, overlayFile, shouldStamp, stampSource, stampTemplate, startServer };
package/dist/index.d.ts CHANGED
@@ -117,6 +117,8 @@ interface RunCommit {
117
117
  };
118
118
  /** Why an auto-commit didn't happen. */
119
119
  note?: string;
120
+ /** No longer changed and never committed: the edits were undone (reverted or discarded). */
121
+ undone?: boolean;
120
122
  }
121
123
  interface RunnerInfo {
122
124
  agents: RunnerAgent[];
@@ -134,4 +136,14 @@ interface RunnerInfo {
134
136
  */
135
137
  declare function isLocalRequest(req: IncomingMessage): boolean;
136
138
 
137
- export { type AgentStatus, type LimnoServer, type LimnoService, type LimnoServiceOptions, ROUTE, type RunnerAgent, type RunnerAgentId, type RunnerInfo, type RunnerLogEntry, type RunnerState, SOURCE_FILES, TEMPLATE_FILES, createLimnoService, isLocalRequest, overlayFile, shouldStamp, stampSource, stampTemplate, startServer };
139
+ /**
140
+ * A tiny inline script for the top of the page, ahead of the app's own code. Inside Limno's
141
+ * edit frame only, while the overlay is selecting or commenting (`__limnoHoldFocus`), it keeps
142
+ * "the page lost focus" from the app: an open menu, popover or select then stays open while it's
143
+ * edited, and through tab or app switches. Window listeners run in the order they were added, so
144
+ * this has to be registered before the app's; the overlay installs the same guard later as a
145
+ * fallback (and skips it when this one is there).
146
+ */
147
+ declare const FRAME_FOCUS_GUARD = "(function(){if(window.name!==\"limno-frame\"||window.__limnoGuard)return;window.__limnoGuard=true;var g=function(e){if(!window.__limnoHoldFocus||e.relatedTarget)return;e.stopImmediatePropagation();};window.addEventListener(\"blur\",g,true);window.addEventListener(\"focusout\",g,true);document.addEventListener(\"visibilitychange\",function(e){if(window.__limnoHoldFocus)e.stopImmediatePropagation();},true);})();";
148
+
149
+ export { type AgentStatus, FRAME_FOCUS_GUARD, type LimnoServer, type LimnoService, type LimnoServiceOptions, ROUTE, type RunnerAgent, type RunnerAgentId, type RunnerInfo, type RunnerLogEntry, type RunnerState, SOURCE_FILES, TEMPLATE_FILES, createLimnoService, isLocalRequest, overlayFile, shouldStamp, stampSource, stampTemplate, startServer };
package/dist/index.js CHANGED
@@ -248,6 +248,11 @@ async function uncommitted(root, files) {
248
248
  const top = (await git(root, ["rev-parse", "--show-toplevel"])).out.trim();
249
249
  return files.filter((f) => dirty.has(top ? path2.relative(top, path2.join(root, f)) : f) || dirty.has(f));
250
250
  }
251
+ async function committedSince(root, files, since) {
252
+ if (!files.length) return false;
253
+ const r = await git(root, ["log", `--since=${Math.floor(since / 1e3)}`, "--format=%h", "--", ...files]);
254
+ return r.code === 0 && r.out.trim().length > 0;
255
+ }
251
256
  async function commitFiles(root, files, message) {
252
257
  if (!await isGitRepo(root)) return { ok: false, error: "This project isn\u2019t a git repository." };
253
258
  for (const marker of ["MERGE_HEAD", "rebase-merge", "rebase-apply", "CHERRY_PICK_HEAD"]) {
@@ -659,7 +664,9 @@ function createRunner(root, folder, onState) {
659
664
  return {
660
665
  async info(force) {
661
666
  if (state.commit && !state.commit.committed && state.commit.uncommitted.length && !child) {
662
- state.commit = { ...state.commit, uncommitted: await uncommitted(root, state.files) };
667
+ const left = await uncommitted(root, state.files);
668
+ const undone = !left.length && !await committedSince(root, state.files, state.startedAt ?? 0);
669
+ state.commit = { ...state.commit, uncommitted: left, undone };
663
670
  }
664
671
  return { agents: await detectAgents(force), allowed: allowed(), autoCommit: autoCommitOn(), state };
665
672
  },
@@ -918,6 +925,7 @@ data: ${JSON.stringify(data)}
918
925
  let watcher = null;
919
926
  let watchedIno = -1;
920
927
  let debounce;
928
+ const pending = /* @__PURE__ */ new Set();
921
929
  function startWatching() {
922
930
  watcher?.close();
923
931
  watcher = null;
@@ -925,10 +933,13 @@ data: ${JSON.stringify(data)}
925
933
  watchedIno = statSync(folder).ino;
926
934
  watcher = watch(folder, (_event, name) => {
927
935
  if (!name || name.endsWith(".tmp")) return;
936
+ pending.add(String(name));
928
937
  clearTimeout(debounce);
929
938
  debounce = setTimeout(() => {
930
- if (name === "changeset.json") onChangeSetFile();
931
- if (name === "agent.json") pushAgent();
939
+ const names = new Set(pending);
940
+ pending.clear();
941
+ if (names.has("changeset.json")) onChangeSetFile();
942
+ if (names.has("agent.json")) pushAgent();
932
943
  }, 40);
933
944
  });
934
945
  watcher.unref?.();
@@ -1165,7 +1176,11 @@ var SOURCE_FILES = /\.(jsx|tsx|js|mjs)$/;
1165
1176
  function shouldStamp(file, include = SOURCE_FILES) {
1166
1177
  return include.test(file) && !/[\\/]node_modules[\\/]/.test(file) && !file.startsWith("\0");
1167
1178
  }
1179
+
1180
+ // src/frameGuard.ts
1181
+ var FRAME_FOCUS_GUARD = `(function(){if(window.name!=="limno-frame"||window.__limnoGuard)return;window.__limnoGuard=true;var g=function(e){if(!window.__limnoHoldFocus||e.relatedTarget)return;e.stopImmediatePropagation();};window.addEventListener("blur",g,true);window.addEventListener("focusout",g,true);document.addEventListener("visibilitychange",function(e){if(window.__limnoHoldFocus)e.stopImmediatePropagation();},true);})();`;
1168
1182
  export {
1183
+ FRAME_FOCUS_GUARD,
1169
1184
  ROUTE,
1170
1185
  SOURCE_FILES,
1171
1186
  TEMPLATE_FILES,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@limno/core",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "Limno's framework-neutral dev tooling: JSX source stamping, the change-set store and the endpoints the editor talks to.",
5
5
  "license": "FSL-1.1-MIT",
6
6
  "type": "module",
@@ -17,12 +17,7 @@
17
17
  "files": [
18
18
  "dist"
19
19
  ],
20
- "repository": {
21
- "type": "git",
22
- "url": "git+https://github.com/armie3/limno.git",
23
- "directory": "packages/core"
24
- },
25
- "homepage": "https://github.com/armie3/limno#readme",
20
+ "homepage": "https://limno.dev",
26
21
  "keywords": [
27
22
  "limno",
28
23
  "design",
@@ -42,9 +37,9 @@
42
37
  "dependencies": {
43
38
  "@babel/parser": "^7.29.0",
44
39
  "@babel/traverse": "^7.29.0",
45
- "@limno/mcp": "0.1.5",
46
- "@limno/overlay": "0.1.11",
47
- "@limno/spec": "0.1.5",
40
+ "@limno/mcp": "0.1.7",
41
+ "@limno/overlay": "0.1.13",
42
+ "@limno/spec": "0.1.7",
48
43
  "launch-editor": "^2.14.1",
49
44
  "magic-string": "^0.30.17"
50
45
  },