paneltir 0.7.0 → 0.8.0

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/INSTALL.md CHANGED
@@ -19,7 +19,7 @@ identity** — never another project's.
19
19
  npm install paneltir
20
20
 
21
21
  # or, without npm access, straight from the repository:
22
- # npm install github:daifukus/paneltir#v0.7.0
22
+ # npm install github:daifukus/paneltir#v0.8.0
23
23
  ```
24
24
 
25
25
  (Use the tag you were given; never install without pinning a version.)
@@ -181,7 +181,7 @@ identity** — never another project's.
181
181
 
182
182
  ```bash
183
183
  npx paneltir version # version, fingerprint, where it came from
184
- npx paneltir check 0.7.0 # exits non-zero if that is not what is installed
184
+ npx paneltir check 0.8.0 # exits non-zero if that is not what is installed
185
185
  npx paneltir board # read the board and say what is wrong with it
186
186
  ```
187
187
 
package/README.md CHANGED
@@ -54,7 +54,7 @@ tag rather than a branch, so an unfinished push cannot reach a project:
54
54
  npm install paneltir
55
55
 
56
56
  # or, without npm access, straight from the repository:
57
- # npm install github:daifukus/paneltir#v0.7.0
57
+ # npm install github:daifukus/paneltir#v0.8.0
58
58
  ```
59
59
 
60
60
  Installing from Git runs the `prepare` script, which builds `dist/`, so the
package/dist/index.d.ts CHANGED
@@ -872,6 +872,14 @@ interface Preferences {
872
872
  seen?: string[];
873
873
  /** Ids of guide notes already dismissed. */
874
874
  guideDismissed?: string[];
875
+ /**
876
+ * Whether an update card is written as an order rather than a suggestion.
877
+ *
878
+ * It does not make anything automatic — the panel cannot run npm, and this
879
+ * changes `order` on the card it offers to write, nothing else. Off means
880
+ * the card asks; on means it tells.
881
+ */
882
+ updateAsOrder?: boolean;
875
883
  }
876
884
  interface PanelState {
877
885
  v: 2;
@@ -1117,6 +1125,19 @@ interface UiStrings {
1117
1125
  analysis: string;
1118
1126
  /** The Revisions tab. Short, because it sits beside Board and Analysis. */
1119
1127
  history: string;
1128
+ updateTitle: string;
1129
+ updateSubtitle: string;
1130
+ updateCurrent: (version: string) => string;
1131
+ updateAvailable: (version: string) => string;
1132
+ updateUpToDate: string;
1133
+ updateUnknown: string;
1134
+ updateCreateCard: string;
1135
+ updateCardExists: string;
1136
+ updateAsOrder: string;
1137
+ updateAsOrderHow: string;
1138
+ notifUpdate: string;
1139
+ updateCardTitle: (version: string) => string;
1140
+ updateCardBody: (version: string) => string;
1120
1141
  cardDetail: string;
1121
1142
  panelSettings: string;
1122
1143
  title: string;
@@ -1419,6 +1440,14 @@ interface PanelAppProps {
1419
1440
  showLanguage?: boolean;
1420
1441
  /** The revision log: one entry per session, newest first. */
1421
1442
  showHistory?: boolean;
1443
+ /**
1444
+ * Ask the public npm registry whether a newer kit has been published.
1445
+ *
1446
+ * Opt-in, because it is a request leaving the reader's browser and that is
1447
+ * the caller's decision rather than the kit's. Nothing is installed either
1448
+ * way: the panel offers to write a card, and a session acts on it.
1449
+ */
1450
+ checkForUpdates?: boolean;
1422
1451
  /** Endpoint that commits the board back to the repository, when there is one. */
1423
1452
  saveEndpoint?: string;
1424
1453
  /**
@@ -1436,7 +1465,7 @@ interface PanelAppProps {
1436
1465
  fileCount: number;
1437
1466
  };
1438
1467
  }
1439
- declare function PanelApp({ state: initialState, themes, defaultTheme, brand, decoration, className, footer, links, capabilities, extras, showLanguage, showHistory, saveEndpoint, marketplaceEndpoint, marketplaceReport, fingerprint, }: PanelAppProps): React.JSX.Element;
1468
+ declare function PanelApp({ state: initialState, themes, defaultTheme, brand, decoration, className, footer, links, capabilities, extras, showLanguage, showHistory, checkForUpdates, saveEndpoint, marketplaceEndpoint, marketplaceReport, fingerprint, }: PanelAppProps): React.JSX.Element;
1440
1469
 
1441
1470
  /**
1442
1471
  * Applies a board move to the flat card list.
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // src/version.ts
2
- var PANELTIR_VERSION = "0.7.0";
3
- var PANELTIR_FINGERPRINT = "sha256:406af8c690cfa9b2452360712afff3f3fe9605a7bb0aa834238e5187acfc37c0";
4
- var PANELTIR_FILE_COUNT = 151;
2
+ var PANELTIR_VERSION = "0.8.0";
3
+ var PANELTIR_FINGERPRINT = "sha256:a252ef2a015240e0e8463c0ebb0ef7a8af80ad7ade66f2c48e71657500d506d5";
4
+ var PANELTIR_FILE_COUNT = 153;
5
5
  var paneltirBuild = {
6
6
  version: PANELTIR_VERSION,
7
7
  fingerprint: PANELTIR_FINGERPRINT,
@@ -1302,6 +1302,37 @@ function isCard(value) {
1302
1302
  // src/panel/PanelApp.tsx
1303
1303
  import React12, { useCallback as useCallback5, useEffect as useEffect9, useMemo as useMemo5, useState as useState9 } from "react";
1304
1304
 
1305
+ // src/panel/updates.ts
1306
+ var REGISTRY_URL = "https://registry.npmjs.org/paneltir/latest";
1307
+ function isNewer(candidate, current) {
1308
+ const parts = (value) => value.trim().replace(/^v/, "").split(/[-+]/)[0].split(".").map((piece) => Number.parseInt(piece, 10));
1309
+ const left = parts(candidate);
1310
+ const right = parts(current);
1311
+ if (left.some(Number.isNaN) || right.some(Number.isNaN)) return false;
1312
+ for (let i = 0; i < 3; i += 1) {
1313
+ const a = left[i] ?? 0;
1314
+ const b = right[i] ?? 0;
1315
+ if (a !== b) return a > b;
1316
+ }
1317
+ return false;
1318
+ }
1319
+ async function checkForUpdate(current, fetchImpl = fetch) {
1320
+ try {
1321
+ const response = await fetchImpl(REGISTRY_URL, { headers: { accept: "application/json" } });
1322
+ if (!response.ok) return null;
1323
+ const body = await response.json();
1324
+ const latest = typeof body.version === "string" ? body.version : null;
1325
+ if (!latest) return null;
1326
+ return { current, latest, behind: isNewer(latest, current) };
1327
+ } catch {
1328
+ return null;
1329
+ }
1330
+ }
1331
+ function updateCardId(version) {
1332
+ const found = /\d+\.\d+\.\d+/.exec(version);
1333
+ return `paneltir-update-${found ? found[0] : "unknown"}`;
1334
+ }
1335
+
1305
1336
  // src/panel/themes.ts
1306
1337
  var PRESET_FORM = {
1307
1338
  midnight: THEME_FORMS.panel,
@@ -1356,6 +1387,19 @@ var UI = {
1356
1387
  board: "Board",
1357
1388
  analysis: "Analysis",
1358
1389
  history: "Revisions",
1390
+ updateTitle: "The kit this panel runs on",
1391
+ updateSubtitle: "What is installed, and what has been published since.",
1392
+ updateCurrent: (version) => `Installed: v${version}`,
1393
+ updateAvailable: (version) => `v${version} has been published.`,
1394
+ updateUpToDate: "This is the newest published version.",
1395
+ updateUnknown: "The registry could not be reached, so there is nothing to report either way.",
1396
+ updateCreateCard: "Add a card asking for it",
1397
+ updateCardExists: "The board already carries a card for this version.",
1398
+ updateAsOrder: "Write it as an order, not a suggestion",
1399
+ updateAsOrderHow: "This does not update anything on its own \u2014 the panel cannot run npm. It decides whether the card it writes asks for the update or instructs it.",
1400
+ notifUpdate: "A newer Paneltir has been published",
1401
+ updateCardTitle: (version) => `Update Paneltir to v${version}`,
1402
+ updateCardBody: (version) => `Run \`npm install paneltir@${version}\` in this project, then check the panel still builds and renders. The version in use is reported by \`paneltirBuild\`, so it can be confirmed rather than assumed.`,
1359
1403
  cardDetail: "Card",
1360
1404
  panelSettings: "Panel settings",
1361
1405
  title: "Title",
@@ -1617,6 +1661,19 @@ var UI = {
1617
1661
  board: "Tablero",
1618
1662
  analysis: "An\xE1lisis",
1619
1663
  history: "Revisiones",
1664
+ updateTitle: "El kit sobre el que corre este panel",
1665
+ updateSubtitle: "Qu\xE9 hay instalado, y qu\xE9 se ha publicado desde entonces.",
1666
+ updateCurrent: (version) => `Instalada: v${version}`,
1667
+ updateAvailable: (version) => `Se ha publicado la v${version}.`,
1668
+ updateUpToDate: "Esta es la versi\xF3n publicada m\xE1s reciente.",
1669
+ updateUnknown: "No se pudo consultar el registro, as\xED que no hay nada que decir en ning\xFAn sentido.",
1670
+ updateCreateCard: "A\xF1adir una tarjeta pidi\xE9ndolo",
1671
+ updateCardExists: "El tablero ya lleva una tarjeta para esta versi\xF3n.",
1672
+ updateAsOrder: "Escribirla como orden, no como sugerencia",
1673
+ updateAsOrderHow: "Esto no actualiza nada por su cuenta \u2014 el panel no puede ejecutar npm. Decide si la tarjeta que escribe pide la actualizaci\xF3n o la manda.",
1674
+ notifUpdate: "Se ha publicado un Paneltir m\xE1s nuevo",
1675
+ updateCardTitle: (version) => `Actualizar Paneltir a la v${version}`,
1676
+ updateCardBody: (version) => `Ejecuta \`npm install paneltir@${version}\` en este proyecto y comprueba que el panel sigue construy\xE9ndose y dibuj\xE1ndose. La versi\xF3n en uso la reporta \`paneltirBuild\`, as\xED que se puede confirmar en vez de darla por hecha.`,
1620
1677
  cardDetail: "Tarjeta",
1621
1678
  panelSettings: "Ajustes del panel",
1622
1679
  title: "T\xEDtulo",
@@ -2685,6 +2742,7 @@ function PanelApp({
2685
2742
  extras,
2686
2743
  showLanguage,
2687
2744
  showHistory,
2745
+ checkForUpdates,
2688
2746
  saveEndpoint,
2689
2747
  marketplaceEndpoint,
2690
2748
  marketplaceReport,
@@ -2702,6 +2760,7 @@ function PanelApp({
2702
2760
  const [filter, setFilter] = useState9("all");
2703
2761
  const [openCardId, setOpenCardId] = useState9(null);
2704
2762
  const [settingsOpen, setSettingsOpen] = useState9(false);
2763
+ const [update, setUpdate] = useState9(null);
2705
2764
  const [helpOpen, setHelpOpen] = useState9(false);
2706
2765
  const [setup, setSetup] = useState9(null);
2707
2766
  const [signOut, setSignOut] = useState9("idle");
@@ -2810,6 +2869,15 @@ function PanelApp({
2810
2869
  onOpen: () => setOpenCardId(card.id)
2811
2870
  });
2812
2871
  }
2872
+ if (update?.behind && !state.cards.some((card) => card.id === updateCardId(update.latest))) {
2873
+ items.push({
2874
+ id: `paneltir-update:${update.latest}`,
2875
+ tone: "info",
2876
+ title: ui.notifUpdate,
2877
+ detail: ui.updateAvailable(update.latest),
2878
+ onOpen: () => setSettingsOpen(true)
2879
+ });
2880
+ }
2813
2881
  if (state.importedTheme?.refreshRequested) {
2814
2882
  items.push({
2815
2883
  id: `theme-refresh:${state.importedTheme.refreshRequested}`,
@@ -2889,6 +2957,23 @@ function PanelApp({
2889
2957
  },
2890
2958
  [state.areas]
2891
2959
  );
2960
+ const addUpdateCard = useCallback5(() => {
2961
+ if (!update?.behind) return;
2962
+ const id = updateCardId(update.latest);
2963
+ if (state.cards.some((card2) => card2.id === id)) return;
2964
+ const card = {
2965
+ ...emptyCard("next", state.areas[0]?.id ?? "general"),
2966
+ id,
2967
+ title: { en: UI.en.updateCardTitle(update.latest), es: UI.es.updateCardTitle(update.latest) },
2968
+ body: { en: UI.en.updateCardBody(update.latest), es: UI.es.updateCardBody(update.latest) },
2969
+ priority: "medium",
2970
+ risk: "low",
2971
+ owner: "claude",
2972
+ order: state.preferences?.updateAsOrder === true
2973
+ };
2974
+ setState((current) => ({ ...current, updatedAt: today(), cards: [...current.cards, card] }));
2975
+ setOpenCardId(id);
2976
+ }, [update, state.cards, state.areas, state.preferences?.updateAsOrder]);
2892
2977
  const moveCard = useCallback5(
2893
2978
  (move) => {
2894
2979
  setState((current) => {
@@ -3008,6 +3093,16 @@ function PanelApp({
3008
3093
  setMarketAsked(true);
3009
3094
  loadMarketplace(false);
3010
3095
  }, [view, marketAsked, marketplaceEndpoint, loadMarketplace]);
3096
+ useEffect9(() => {
3097
+ if (!checkForUpdates || !fingerprint?.version) return;
3098
+ let cancelled = false;
3099
+ void checkForUpdate(fingerprint.version).then((status2) => {
3100
+ if (!cancelled) setUpdate(status2);
3101
+ });
3102
+ return () => {
3103
+ cancelled = true;
3104
+ };
3105
+ }, [checkForUpdates, fingerprint?.version]);
3011
3106
  useEffect9(() => {
3012
3107
  if (!helpOpen || !saveEndpoint || setup) return;
3013
3108
  let cancelled = false;
@@ -3512,6 +3607,23 @@ function PanelApp({
3512
3607
  /* @__PURE__ */ jsx19("p", { className: "pt-marketplace__detail", children: ui.importedRefreshHow })
3513
3608
  ] })
3514
3609
  ] }),
3610
+ checkForUpdates && fingerprint?.version && /* @__PURE__ */ jsxs17(DetailSection, { title: ui.updateTitle, subtitle: ui.updateSubtitle, children: [
3611
+ /* @__PURE__ */ jsx19("p", { className: "pt-panel-text", children: ui.updateCurrent(fingerprint.version) }),
3612
+ /* @__PURE__ */ jsx19("p", { className: "pt-panel-text", style: { marginTop: 8 }, children: !update ? ui.updateUnknown : update.behind ? ui.updateAvailable(update.latest) : ui.updateUpToDate }),
3613
+ update?.behind && (state.cards.some((card) => card.id === updateCardId(update.latest)) ? /* @__PURE__ */ jsx19("p", { className: "pt-panel-text", style: { marginTop: 8 }, children: ui.updateCardExists }) : /* @__PURE__ */ jsx19("div", { className: "pt-panel-row", style: { marginTop: 12 }, children: /* @__PURE__ */ jsx19("button", { type: "button", className: "pt-btn", onClick: addUpdateCard, children: ui.updateCreateCard }) })),
3614
+ /* @__PURE__ */ jsxs17("label", { className: "pt-panel-row", style: { marginTop: 12 }, children: [
3615
+ /* @__PURE__ */ jsx19(
3616
+ "input",
3617
+ {
3618
+ type: "checkbox",
3619
+ checked: state.preferences?.updateAsOrder === true,
3620
+ onChange: (event) => setPreference("updateAsOrder", event.target.checked)
3621
+ }
3622
+ ),
3623
+ /* @__PURE__ */ jsx19("span", { className: "pt-panel-text", children: ui.updateAsOrder })
3624
+ ] }),
3625
+ /* @__PURE__ */ jsx19("p", { className: "pt-marketplace__detail", children: ui.updateAsOrderHow })
3626
+ ] }),
3515
3627
  fingerprint && /* @__PURE__ */ jsxs17(DetailSection, { title: ui.fingerprint, subtitle: ui.fingerprintSubtitle, children: [
3516
3628
  /* @__PURE__ */ jsx19("p", { className: "pt-panel-mono", children: fingerprint.hash }),
3517
3629
  /* @__PURE__ */ jsx19("p", { className: "pt-panel-text", style: { marginTop: 8 }, children: ui.trackedFiles(fingerprint.fileCount, fingerprint.version) })
package/fingerprint.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "paneltir",
3
- "version": "0.7.0",
4
- "hash": "sha256:406af8c690cfa9b2452360712afff3f3fe9605a7bb0aa834238e5187acfc37c0",
5
- "fileCount": 151,
6
- "generatedAt": "2026-09-07T12:33:30.026Z"
3
+ "version": "0.8.0",
4
+ "hash": "sha256:a252ef2a015240e0e8463c0ebb0ef7a8af80ad7ade66f2c48e71657500d506d5",
5
+ "fileCount": 153,
6
+ "generatedAt": "2026-09-07T12:59:14.850Z"
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paneltir",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "A React UI kit for admin panels: page frame, a board with touch-ready drag and drop, detail sheets, and a whole panel in one component. Structure and behaviour are fixed; colour and brand belong to each project.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "DFKlabs (https://DFKlabs.com)",
@@ -64,7 +64,7 @@
64
64
  "check:boards": "node scripts/check-boards.mjs",
65
65
  "demo:install": "npm --prefix examples/demo ci",
66
66
  "demo:build": "npm --prefix examples/demo run build",
67
- "verify": "npm run typecheck && npm run build && npm run icons:check && npm run seo:check && npm run test:auth && npm run test:marketplace && npm run test:init && npm run test:board && npm run check:boards && npm run check:docs && npm run check:package && npm run fingerprint:check",
67
+ "verify": "npm run typecheck && npm run build && npm run icons:check && npm run seo:check && npm run test:auth && npm run test:marketplace && npm run test:init && npm run test:board && npm run test:updates && npm run check:boards && npm run check:docs && npm run check:package && npm run fingerprint:check",
68
68
  "site:install": "npm --prefix site ci",
69
69
  "site:dev": "npm --prefix site run dev",
70
70
  "site:build": "npm --prefix site run build",
@@ -72,6 +72,7 @@
72
72
  "check:docs": "node scripts/check-docs.mjs",
73
73
  "check:package": "node scripts/check-package.mjs",
74
74
  "test:init": "node scripts/test-init.mjs",
75
+ "test:updates": "node scripts/test-updates.mjs",
75
76
  "test:board": "node scripts/test-board.mjs"
76
77
  },
77
78
  "peerDependencies": {