minecodex 0.1.6 → 0.1.8

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "id": "images",
4
- "version": "0.1.6",
4
+ "version": "0.1.8",
5
5
  "label": {
6
6
  "en": "Images",
7
7
  "zh-CN": "图片"
@@ -303,9 +303,9 @@ function layoutMasonry() {
303
303
  const cards = [...grid.children];
304
304
  if (!cards.length) return;
305
305
 
306
- // Keep DOM writes and geometry reads in separate batches so the browser only
307
- // needs to resolve layout once, even for a full page of boards.
308
- for (const card of cards) card.style.gridRowEnd = "auto";
306
+ // Existing spans do not affect the intrinsic card boxes because the grid
307
+ // aligns items to the start. Clearing them here would collapse the 1px row
308
+ // grid and clamp the user's scroll position before the new spans are applied.
309
309
  const blockGap = Number.parseFloat(getComputedStyle(cards[0]).marginBottom);
310
310
  const spans = cards.map((card) => (
311
311
  Math.ceil((card.getBoundingClientRect().height + blockGap) / rowHeight)
@@ -617,6 +617,7 @@ h1,
617
617
 
618
618
  .thread-board-preview img {
619
619
  width: 100%;
620
+ height: auto;
620
621
  min-height: 76px;
621
622
  aspect-ratio: 16 / 6;
622
623
  display: block;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "id": "model-slider",
4
- "version": "0.1.6",
4
+ "version": "0.1.8",
5
5
  "label": {
6
6
  "en": "Model Slider",
7
7
  "zh-CN": "模型滑条"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "id": "notes",
4
- "version": "0.1.6",
4
+ "version": "0.1.8",
5
5
  "label": {
6
6
  "en": "Notes",
7
7
  "zh-CN": "笔记"
@@ -1,4 +1,4 @@
1
- const params = new URLSearchParams(location.search);
1
+ const params = new URL(document.baseURI).searchParams;
2
2
  const SUPPORTED_LOCALES = new Set(["en", "zh-CN"]);
3
3
 
4
4
  function normalizeLocale(value) {
@@ -835,7 +835,7 @@ body[data-surface="editor"] #app {
835
835
  .modal-editor-fields input:focus-visible,
836
836
  .modal-editor-fields textarea:focus-visible {
837
837
  outline: none;
838
- box-shadow: inset 0 0 0 2px var(--notes-focus);
838
+ box-shadow: none;
839
839
  }
840
840
 
841
841
  .modal-command:focus-visible {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "minecodex",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Lightweight, local-first plugins for the Codex desktop app.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -51,12 +51,25 @@ function declaredSurfaceUrls(feature) {
51
51
  }
52
52
 
53
53
  function requireDeclaredLoopbackSurface(feature, value) {
54
- if (typeof value !== "string" || !declaredSurfaceUrls(feature).has(value)) {
54
+ if (typeof value !== "string") {
55
55
  throw Object.assign(new Error("Surface URL is not declared by this feature"), {
56
56
  code: "SURFACE_URL_NOT_ALLOWED",
57
57
  });
58
58
  }
59
59
  const url = new URL(value);
60
+ const matchesDeclaredDocument = Array.from(declaredSurfaceUrls(feature), (declared) => new URL(declared))
61
+ .some((declared) => (
62
+ declared.origin === url.origin
63
+ && declared.username === url.username
64
+ && declared.password === url.password
65
+ && declared.pathname === url.pathname
66
+ && declared.hash === url.hash
67
+ ));
68
+ if (!matchesDeclaredDocument) {
69
+ throw Object.assign(new Error("Surface URL is not declared by this feature"), {
70
+ code: "SURFACE_URL_NOT_ALLOWED",
71
+ });
72
+ }
60
73
  if (url.protocol !== "http:" || !LOOPBACK_SURFACE_HOSTS.has(url.hostname)) {
61
74
  throw Object.assign(new Error("Surface URL must use an exact loopback HTTP origin"), {
62
75
  code: "SURFACE_URL_NOT_ALLOWED",
@@ -235,15 +248,12 @@ export function createInjectionSource(features, {
235
248
  const pageSurfaces = new Map();
236
249
  const pinnedSurfaces = new Map();
237
250
  const surfaceRecords = new Map();
238
- const openTabs = new Map();
239
251
  const nativeDetailComponents = new Map();
240
252
  const nativeDetailSurfaceKeys = new Map();
241
253
  const nativeOpenTabSessions = new Set();
242
254
  const summaryStates = new Map();
243
255
  let activePageFeatureId = null;
244
256
  let activePinnedFeatureId = null;
245
- let activeTabKey = null;
246
- let tabHost = null;
247
257
  let ensureQueued = false;
248
258
  let ensureFrame = null;
249
259
  let ensureTimer = null;
@@ -2541,7 +2551,7 @@ export function createInjectionSource(features, {
2541
2551
 
2542
2552
  closePromptPreview({ immediate: true });
2543
2553
  closeModalSurface();
2544
- const editorUrl = new URL(sourceRecord.frame.src);
2554
+ const editorUrl = new URL(sourceRecord.surfaceUrl);
2545
2555
  editorUrl.searchParams.set("surface", "editor");
2546
2556
  editorUrl.searchParams.set("kind", kind);
2547
2557
  if (typeof payload.id === "string" && payload.id) editorUrl.searchParams.set("id", payload.id);
@@ -2759,12 +2769,32 @@ export function createInjectionSource(features, {
2759
2769
  return null;
2760
2770
  }
2761
2771
 
2772
+ async function waitForCurrentTaskScope() {
2773
+ const deadline = performance.now() + 2_000;
2774
+ do {
2775
+ const scope = currentTaskScope();
2776
+ if (scope) return scope;
2777
+ await new Promise((resolve) => setTimeout(resolve, 50));
2778
+ } while (performance.now() < deadline);
2779
+ return null;
2780
+ }
2781
+
2782
+ async function waitForNativeTabModuleUrl() {
2783
+ const deadline = performance.now() + 2_000;
2784
+ do {
2785
+ const moduleUrl = nativeTabModuleUrl();
2786
+ if (moduleUrl) return moduleUrl;
2787
+ await new Promise((resolve) => setTimeout(resolve, 50));
2788
+ } while (performance.now() < deadline);
2789
+ return null;
2790
+ }
2791
+
2762
2792
  async function loadNativeTabCapability() {
2763
2793
  if (nativeTabCapability) return nativeTabCapability;
2764
2794
  if (nativeTabCapabilityPromise) return nativeTabCapabilityPromise;
2765
2795
  nativeTabCapabilityPromise = (async () => {
2766
2796
  try {
2767
- const moduleUrl = nativeTabModuleUrl();
2797
+ const moduleUrl = await waitForNativeTabModuleUrl();
2768
2798
  if (!moduleUrl) throw new Error("Codex app-initial modulepreload was not found");
2769
2799
  const appModule = await import(moduleUrl);
2770
2800
  if (typeof appModule.Gtt !== "function") {
@@ -2797,7 +2827,9 @@ export function createInjectionSource(features, {
2797
2827
  return null;
2798
2828
  }
2799
2829
  })();
2800
- return nativeTabCapabilityPromise;
2830
+ const capability = await nativeTabCapabilityPromise;
2831
+ if (!capability) nativeTabCapabilityPromise = null;
2832
+ return capability;
2801
2833
  }
2802
2834
 
2803
2835
  function nativeDetailIcon(feature, detail, jsx) {
@@ -2894,7 +2926,7 @@ export function createInjectionSource(features, {
2894
2926
  async function openNativeDetailTab(feature, detail) {
2895
2927
  const capability = await loadNativeTabCapability();
2896
2928
  if (!capability) return null;
2897
- const scope = currentTaskScope();
2929
+ const scope = await waitForCurrentTaskScope();
2898
2930
  if (!scope) {
2899
2931
  nativeTabCapabilityError = new Error("The current Codex Task scope was not found");
2900
2932
  return null;
@@ -2936,14 +2968,12 @@ export function createInjectionSource(features, {
2936
2968
 
2937
2969
  nativeTabCapabilityError = null;
2938
2970
  if (focusedExisting) reloadNativeDetailIfUnready(feature, detail);
2939
- const fallbackKey = `${feature.id}:${detail.id}`;
2940
- if (openTabs.has(fallbackKey)) closeDetailTab(fallbackKey);
2941
2971
  return { tabId, focusedExisting, presentation: "native" };
2942
2972
  }
2943
2973
 
2944
2974
  function detailTabCapability() {
2945
2975
  return {
2946
- presentation: nativeTabCapability ? "native" : "fallback",
2976
+ presentation: nativeTabCapability ? "native" : "unavailable",
2947
2977
  moduleAsset: nativeTabCapability?.moduleAsset ?? null,
2948
2978
  error: nativeTabCapabilityError?.message ?? null,
2949
2979
  openNativeTabs: nativeOpenTabSessions.size,
@@ -2961,185 +2991,14 @@ export function createInjectionSource(features, {
2961
2991
  nativeDetailSurfaceKeys.clear();
2962
2992
  }
2963
2993
 
2964
- function updateTabHostGeometry() {
2965
- if (!tabHost?.isConnected) return;
2966
- const width = Math.min(620, Math.max(360, innerWidth * 0.44));
2967
- tabHost.style.width = `${Math.min(width, Math.max(300, innerWidth - 72))}px`;
2968
- }
2969
-
2970
- function createTabHost() {
2971
- const host = document.createElement("aside");
2972
- host.setAttribute("data-codex-personal-tab-host", "right");
2973
- host.setAttribute("aria-label", locale === "zh-CN" ? "CodexNotes 工作区标签页" : "CodexNotes workspace tabs");
2974
- host.style.cssText = [
2975
- "position:fixed",
2976
- "top:46px",
2977
- "bottom:0",
2978
- "right:0",
2979
- "z-index:42",
2980
- "display:flex",
2981
- "flex-direction:column",
2982
- "min-width:300px",
2983
- "background:var(--color-token-main-surface-primary)",
2984
- "border-left:0.5px solid var(--color-border)",
2985
- "box-shadow:-8px 0 16px -12px rgba(0,0,0,.24)",
2986
- ].join(";");
2987
- const header = document.createElement("div");
2988
- header.style.cssText = [
2989
- "height:46px",
2990
- "display:flex",
2991
- "align-items:center",
2992
- "padding:0 8px",
2993
- "flex:0 0 auto",
2994
- "background:var(--color-token-main-surface-primary)",
2995
- ].join(";");
2996
- const strip = document.createElement("div");
2997
- strip.setAttribute("data-app-shell-tab-strip-controller", "right");
2998
- strip.setAttribute("data-codex-personal-tab-strip", "true");
2999
- strip.setAttribute("role", "tablist");
3000
- strip.style.cssText = "display:flex;align-items:center;gap:4px;min-width:0;flex:1;overflow-x:auto;height:100%";
3001
- header.append(strip);
3002
- const panels = document.createElement("div");
3003
- panels.setAttribute("data-codex-personal-tab-panels", "true");
3004
- panels.style.cssText = "position:relative;min-height:0;flex:1;background:var(--color-token-main-surface-primary)";
3005
- host.append(header, panels);
3006
- document.body.append(host);
3007
- tabHost = host;
3008
- updateTabHostGeometry();
3009
- return host;
3010
- }
3011
-
3012
- function activateTab(key) {
3013
- const record = openTabs.get(key);
3014
- if (!record) return;
3015
- activeTabKey = key;
3016
- for (const [candidateKey, candidate] of openTabs) {
3017
- const active = candidateKey === key;
3018
- candidate.tabButton.setAttribute("aria-selected", String(active));
3019
- candidate.tab.style.setProperty(
3020
- "--app-shell-tab-background",
3021
- active
3022
- ? "color-mix(in srgb, var(--color-text-foreground) 5%, var(--color-token-main-surface-primary))"
3023
- : "transparent",
3024
- );
3025
- candidate.panel.hidden = !active;
3026
- }
3027
- record.tabButton.focus({ preventScroll: true });
3028
- queueTheme();
3029
- }
3030
-
3031
- function closeDetailTab(key) {
3032
- const record = openTabs.get(key);
3033
- if (!record) return;
3034
- const keys = Array.from(openTabs.keys());
3035
- const index = keys.indexOf(key);
3036
- record.tab.remove();
3037
- record.panel.remove();
3038
- removeSurfaceRecord(record.surfaceRecordKey);
3039
- openTabs.delete(key);
3040
- if (openTabs.size === 0) {
3041
- tabHost?.remove();
3042
- tabHost = null;
3043
- activeTabKey = null;
3044
- updateResponsiveSummaryPresentation();
3045
- return;
3046
- }
3047
- if (activeTabKey === key) {
3048
- const remainingKeys = Array.from(openTabs.keys());
3049
- activateTab(remainingKeys[Math.min(index, remainingKeys.length - 1)]);
3050
- }
3051
- updateResponsiveSummaryPresentation();
3052
- }
3053
-
3054
- function createDetailTab(feature, detail) {
3055
- const host = tabHost?.isConnected ? tabHost : createTabHost();
3056
- const strip = host.querySelector("[data-codex-personal-tab-strip]");
3057
- const panels = host.querySelector("[data-codex-personal-tab-panels]");
3058
- const key = `${feature.id}:${detail.id}`;
3059
- const stableId = `${feature.id}-${detail.id}`;
3060
-
3061
- const tab = document.createElement("div");
3062
- tab.setAttribute("data-app-shell-tab-controller", "right");
3063
- tab.setAttribute("data-tab-id", stableId);
3064
- tab.style.cssText = [
3065
- "position:relative",
3066
- "display:flex",
3067
- "align-items:center",
3068
- "min-width:90px",
3069
- "max-width:160px",
3070
- "height:28px",
3071
- "padding:0 4px 0 8px",
3072
- "border-radius:8px",
3073
- "background:var(--app-shell-tab-background,transparent)",
3074
- "flex:1 0 110px",
3075
- ].join(";");
3076
- const tabButton = document.createElement("button");
3077
- tabButton.type = "button";
3078
- tabButton.setAttribute("role", "tab");
3079
- tabButton.setAttribute("aria-selected", "false");
3080
- tabButton.style.cssText = "display:flex;align-items:center;gap:7px;min-width:0;flex:1;height:100%;padding:0;background:transparent;border:0;color:var(--color-text-foreground);cursor:pointer";
3081
- const icon = createMarkupIcon(detail.icon ?? feature.icon, "icon-xs", 16);
3082
- if (icon) tabButton.append(icon);
3083
- const label = document.createElement("span");
3084
- label.textContent = detail.label;
3085
- label.style.cssText = "min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap";
3086
- tabButton.append(label);
3087
- tabButton.addEventListener("click", () => activateTab(key));
3088
- const close = document.createElement("button");
3089
- close.type = "button";
3090
- close.textContent = "×";
3091
- close.setAttribute("data-app-shell-tab-close-button", "true");
3092
- close.setAttribute(
3093
- "aria-label",
3094
- locale === "zh-CN" ? `关闭${detail.label}标签页` : `Close ${detail.label} tab`,
3095
- );
3096
- close.style.cssText = "width:20px;height:20px;border:0;border-radius:6px;padding:0;background:transparent;color:var(--color-text-foreground-tertiary);font-size:18px;line-height:18px;cursor:pointer";
3097
- close.addEventListener("click", (event) => {
3098
- event.stopPropagation();
3099
- closeDetailTab(key);
3100
- });
3101
- tab.append(tabButton, close);
3102
-
3103
- const panel = document.createElement("div");
3104
- panel.setAttribute("role", "tabpanel");
3105
- panel.setAttribute("aria-label", detail.label);
3106
- panel.setAttribute("data-app-shell-tab-panel-controller", "right");
3107
- panel.setAttribute("data-tab-id", stableId);
3108
- panel.style.cssText = "position:absolute;inset:0;min-height:0;background:var(--color-token-main-surface-primary)";
3109
- const frame = createFrame(feature.id, detail.label);
3110
- panel.append(frame);
3111
- strip.append(tab);
3112
- panels.append(panel);
3113
- const recordKey = surfaceKey(feature.id, "detail", detail.id);
3114
- registerSurface(recordKey, feature, "detail", detail.surfaceUrl, panel, frame, detail.id);
3115
- const record = { key, feature, detail, tab, tabButton, close, panel, frame, surfaceRecordKey: recordKey };
3116
- openTabs.set(key, record);
3117
- return record;
3118
- }
3119
-
3120
- function openFallbackDetailTab(feature, detail) {
3121
- const key = `${feature.id}:${detail.id}`;
3122
- const existing = openTabs.has(key);
3123
- if (!existing) createDetailTab(feature, detail);
3124
- else reloadSurfaceIfUnready(
3125
- surfaceKey(feature.id, "detail", detail.id),
3126
- detail.surfaceUrl,
3127
- );
3128
- activateTab(key);
3129
- updateResponsiveSummaryPresentation();
3130
- return {
3131
- tabId: `${feature.id}-${detail.id}`,
3132
- focusedExisting: existing,
3133
- presentation: "fallback",
3134
- };
3135
- }
3136
-
3137
2994
  async function openDetailTab(featureId, detailId) {
3138
2995
  const feature = featureById.get(featureId);
3139
2996
  const detail = feature?.detailTabs?.find((candidate) => candidate.id === detailId);
3140
2997
  if (!feature || !detail) throw new Error("The requested detail Tab is not registered");
3141
- return await openNativeDetailTab(feature, detail)
3142
- ?? openFallbackDetailTab(feature, detail);
2998
+ const result = await openNativeDetailTab(feature, detail);
2999
+ if (!result) throw nativeTabCapabilityError
3000
+ ?? new Error("Codex native right-panel tabs are unavailable");
3001
+ return result;
3143
3002
  }
3144
3003
 
3145
3004
  function openThread(threadId) {
@@ -3295,7 +3154,6 @@ export function createInjectionSource(features, {
3295
3154
  checkThreadChange();
3296
3155
  updateSidebarSelectedState();
3297
3156
  updatePageSurfacePositions();
3298
- updateTabHostGeometry();
3299
3157
  ensureMainContentObserver();
3300
3158
  updateResponsiveSummaryPresentation();
3301
3159
  const nativeToggle = nativeSummaryButton();
@@ -3436,8 +3294,7 @@ export function createInjectionSource(features, {
3436
3294
  const ownEntry = event.target.closest?.(`[${entryMarker}="${activePinnedFeatureId}"]`);
3437
3295
  const rightWorkspaceInteraction = event.target.closest?.(
3438
3296
  '[data-app-shell-tab-strip-controller="right"],'
3439
- + '[data-app-shell-tab-panel-controller="right"],'
3440
- + '[data-codex-personal-tab-host="right"]',
3297
+ + '[data-app-shell-tab-panel-controller="right"]',
3441
3298
  );
3442
3299
  const promptPreviewInteraction = event.target.closest?.(`[${promptPreviewMarker}]`);
3443
3300
  if (
@@ -3525,7 +3382,6 @@ export function createInjectionSource(features, {
3525
3382
  window.addEventListener("resize", () => {
3526
3383
  closePromptPreview({ immediate: true });
3527
3384
  updatePageSurfacePositions();
3528
- updateTabHostGeometry();
3529
3385
  updateResponsiveSummaryPresentation();
3530
3386
  }, { signal: lifetime.signal });
3531
3387
  const ensureObserver = new MutationObserver(queueEnsure);
@@ -3581,9 +3437,7 @@ export function createInjectionSource(features, {
3581
3437
  closeModalSurface();
3582
3438
  closeNativeDetailTabs();
3583
3439
  restoreModelSelectorEnhancements();
3584
- tabHost?.remove();
3585
3440
  surfaceRecords.clear();
3586
- openTabs.clear();
3587
3441
  if (window.__codexPersonalRuntime === runtimeApi) delete window.__codexPersonalRuntime;
3588
3442
  },
3589
3443
  };