@filamind-app/core 0.1.3 → 0.1.4

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.d.ts CHANGED
@@ -457,6 +457,42 @@ declare function aggregateSubscriptions(ids: string[]): SubscriptionMap;
457
457
  /** Test/host helper — clear the registry. */
458
458
  declare function _resetRegistry(): void;
459
459
 
460
+ type SurfaceHint = 'dense-desktop' | 'thumb-phone' | 'big-touch';
461
+ /** Derive the layout hint from the surface + viewport width (px). The touch screen is always
462
+ * big-touch; the 3d web UI is dense on wide viewports and a single thumb column when narrow. */
463
+ declare function surfaceHint(target: SurfaceTarget, viewportWidth: number): SurfaceHint;
464
+ /** One slot in the shared dashboard definition. */
465
+ interface DashboardSlot {
466
+ widgetId: string;
467
+ /** ascending display order; slots without it sort after ordered ones */
468
+ order?: number;
469
+ /** hide this widget on these hints (e.g. a dense-only widget hidden on thumb-phone) */
470
+ hideOn?: SurfaceHint[];
471
+ }
472
+ /** The single serializable dashboard definition (persisted in UserSettings, machineUUID-keyed). */
473
+ interface DashboardLayout {
474
+ version: number;
475
+ slots: DashboardSlot[];
476
+ }
477
+ declare const DASHBOARD_LAYOUT_VERSION = 1;
478
+ interface ResolvedDashboard {
479
+ hint: SurfaceHint;
480
+ columns: number;
481
+ /** the widgets to render, in order, already filtered for this hint */
482
+ widgets: WidgetDefinition[];
483
+ }
484
+ /**
485
+ * Adapt one layout definition + the available widgets to a surface hint.
486
+ *
487
+ * Slots are honoured first (ordered, minus those hidden on this hint); any registered widget the
488
+ * layout doesn't mention is appended (so newly added widgets still appear). A widget a slot hides
489
+ * on this hint stays hidden even though it is available. With no layout, all available widgets
490
+ * render in registry order.
491
+ */
492
+ declare function resolveDashboard(layout: DashboardLayout | undefined, available: WidgetDefinition[], hint: SurfaceHint): ResolvedDashboard;
493
+ /** Coerce an arbitrary blob into a valid DashboardLayout, or undefined. Used by settings migrate. */
494
+ declare function coerceDashboardLayout(raw: unknown): DashboardLayout | undefined;
495
+
460
496
  interface ThemeTokens {
461
497
  bg: string;
462
498
  surface: string;
@@ -582,6 +618,8 @@ interface UserSettings {
582
618
  motifDensity: 'off' | 'subtle' | 'full';
583
619
  /** reduce animation/illustration for low-power or accessibility */
584
620
  reducedMotion: boolean;
621
+ /** the single per-surface dashboard definition (resolved per surface + viewport at render) */
622
+ dashboardLayout?: DashboardLayout;
585
623
  }
586
624
  declare const DEFAULT_SETTINGS: UserSettings;
587
625
  /** Coerce an arbitrary (old / foreign / partial) blob into valid UserSettings — drops unknown keys,
@@ -623,4 +661,4 @@ declare function applySettings(s: UserSettings, el?: {
623
661
  dir: 'ltr' | 'rtl';
624
662
  };
625
663
 
626
- export { type AgentAllow, type AgentEvent, type BackendMessage, type Catalog, CommandSender, type CommandSenderOptions, type ConnectionState, type Connector, type ConnectorCallbacks, DEFAULT_LOCALE, DEFAULT_SETTINGS, DEFAULT_THEME, type DiscoveryOptions, FILAMIND_COMMAND_EVENT, FULL_CONTROL, FilaMindSession, type IdentifyInfo, type KlippyState, LOCALES, type Listener, type LocaleMeta, type LocationLike, type LogEntry, type LogLevel, Logger, MoonrakerClient, type MoonrakerClientOptions, type MoonrakerMethods, NARROW_STATUS, NULL_LOGGER, type NotifyEvent, Observable, type Params, type PluralCategory, type PrinterInfo, type PrinterObjects, PrinterState, type PromptButton, type PromptDialog, type PromptEvent, PromptParser, type QueryResult, REMOTE_MESSAGE_LEVELS, REMOTE_VIEWS, RTL_LOCALES, type RemoteCommand, type RemoteMessageLevel, type RemoteView, type RestorePoint, RestorePoints, type RestoreStore, RpcError, SETTINGS_VERSION, SUBSCRIPTION_CONTRACT_VERSION, type ServerInfo, type SessionOptions, type SettingsPersistence, SettingsStore, type Source, type Stamped, type SubscriptionMap, type SubscriptionTier, type SurfaceTarget, type ThemeName, type ThemeTokens, Translator, UNKNOWN, type UserSettings, type WebSocketLike, type WidgetDefinition, WriteArbiter, type WriteGuard, WriteRefused, _resetRegistry, aggregateSubscriptions, applySettings, applyTheme, deepMerge, deriveCandidates, deriveKlippyState, deriveMachineId, fnv1a, freshness, getWidget, getWidgets, handleAgentCommand, isKlippyLive, isRtl, isStale, localStoragePersistence, localeMeta, memoryPersistence, memoryRestoreStore, mergeSubscriptions, migrate, moonrakerDbPersistence, parseAgentEvent, parseCommand, parseNotifyEvent, pluralCategory, registerWidget, resolveMessage, resolveMoonrakerUrl, roamSettings, stamp, themeToCssVars, themes, tier, translate };
664
+ export { type AgentAllow, type AgentEvent, type BackendMessage, type Catalog, CommandSender, type CommandSenderOptions, type ConnectionState, type Connector, type ConnectorCallbacks, DASHBOARD_LAYOUT_VERSION, DEFAULT_LOCALE, DEFAULT_SETTINGS, DEFAULT_THEME, type DashboardLayout, type DashboardSlot, type DiscoveryOptions, FILAMIND_COMMAND_EVENT, FULL_CONTROL, FilaMindSession, type IdentifyInfo, type KlippyState, LOCALES, type Listener, type LocaleMeta, type LocationLike, type LogEntry, type LogLevel, Logger, MoonrakerClient, type MoonrakerClientOptions, type MoonrakerMethods, NARROW_STATUS, NULL_LOGGER, type NotifyEvent, Observable, type Params, type PluralCategory, type PrinterInfo, type PrinterObjects, PrinterState, type PromptButton, type PromptDialog, type PromptEvent, PromptParser, type QueryResult, REMOTE_MESSAGE_LEVELS, REMOTE_VIEWS, RTL_LOCALES, type RemoteCommand, type RemoteMessageLevel, type RemoteView, type ResolvedDashboard, type RestorePoint, RestorePoints, type RestoreStore, RpcError, SETTINGS_VERSION, SUBSCRIPTION_CONTRACT_VERSION, type ServerInfo, type SessionOptions, type SettingsPersistence, SettingsStore, type Source, type Stamped, type SubscriptionMap, type SubscriptionTier, type SurfaceHint, type SurfaceTarget, type ThemeName, type ThemeTokens, Translator, UNKNOWN, type UserSettings, type WebSocketLike, type WidgetDefinition, WriteArbiter, type WriteGuard, WriteRefused, _resetRegistry, aggregateSubscriptions, applySettings, applyTheme, coerceDashboardLayout, deepMerge, deriveCandidates, deriveKlippyState, deriveMachineId, fnv1a, freshness, getWidget, getWidgets, handleAgentCommand, isKlippyLive, isRtl, isStale, localStoragePersistence, localeMeta, memoryPersistence, memoryRestoreStore, mergeSubscriptions, migrate, moonrakerDbPersistence, parseAgentEvent, parseCommand, parseNotifyEvent, pluralCategory, registerWidget, resolveDashboard, resolveMessage, resolveMoonrakerUrl, roamSettings, stamp, surfaceHint, themeToCssVars, themes, tier, translate };
package/dist/index.js CHANGED
@@ -834,6 +834,58 @@ function _resetRegistry() {
834
834
  registry.clear();
835
835
  }
836
836
 
837
+ // src/registry/dashboard-resolver.ts
838
+ var COLUMNS = {
839
+ "dense-desktop": 3,
840
+ "thumb-phone": 1,
841
+ "big-touch": 2
842
+ };
843
+ function surfaceHint(target, viewportWidth) {
844
+ if (target === "screen") return "big-touch";
845
+ return viewportWidth < 640 ? "thumb-phone" : "dense-desktop";
846
+ }
847
+ var DASHBOARD_LAYOUT_VERSION = 1;
848
+ var HINTS = ["dense-desktop", "thumb-phone", "big-touch"];
849
+ function resolveDashboard(layout, available, hint) {
850
+ const byId = new Map(available.map((w) => [w.id, w]));
851
+ const slots = layout?.slots ?? [];
852
+ const hiddenHere = new Set(
853
+ slots.filter((s) => (s.hideOn ?? []).includes(hint)).map((s) => s.widgetId)
854
+ );
855
+ const used = /* @__PURE__ */ new Set();
856
+ const widgets = [];
857
+ for (const slot of [...slots].filter((s) => !(s.hideOn ?? []).includes(hint)).sort((a, b) => (a.order ?? Number.POSITIVE_INFINITY) - (b.order ?? Number.POSITIVE_INFINITY))) {
858
+ const w = byId.get(slot.widgetId);
859
+ if (w && !used.has(w.id)) {
860
+ widgets.push(w);
861
+ used.add(w.id);
862
+ }
863
+ }
864
+ for (const w of available) {
865
+ if (!used.has(w.id) && !hiddenHere.has(w.id)) widgets.push(w);
866
+ }
867
+ return { hint, columns: COLUMNS[hint], widgets };
868
+ }
869
+ function coerceDashboardLayout(raw) {
870
+ if (!raw || typeof raw !== "object") return void 0;
871
+ const r = raw;
872
+ if (!Array.isArray(r.slots)) return void 0;
873
+ const slots = [];
874
+ for (const item of r.slots) {
875
+ if (!item || typeof item !== "object") continue;
876
+ const s = item;
877
+ if (typeof s.widgetId !== "string" || !s.widgetId) continue;
878
+ const slot = { widgetId: s.widgetId };
879
+ if (typeof s.order === "number" && Number.isFinite(s.order)) slot.order = s.order;
880
+ if (Array.isArray(s.hideOn)) {
881
+ const hideOn = s.hideOn.filter((h) => HINTS.includes(h));
882
+ if (hideOn.length) slot.hideOn = hideOn;
883
+ }
884
+ slots.push(slot);
885
+ }
886
+ return { version: DASHBOARD_LAYOUT_VERSION, slots };
887
+ }
888
+
837
889
  // src/theme/tokens.ts
838
890
  var themes = {
839
891
  // flagship default — obsidian + gold + lapis + turquoise
@@ -1144,7 +1196,7 @@ var DENSITIES = /* @__PURE__ */ new Set(["comfortable", "compact"]);
1144
1196
  var MOTIFS = /* @__PURE__ */ new Set(["off", "subtle", "full"]);
1145
1197
  function migrate(raw) {
1146
1198
  const r = raw && typeof raw === "object" ? raw : {};
1147
- return {
1199
+ const out = {
1148
1200
  version: SETTINGS_VERSION,
1149
1201
  theme: typeof r.theme === "string" && r.theme in themes ? r.theme : DEFAULT_SETTINGS.theme,
1150
1202
  locale: typeof r.locale === "string" && LOCALES.some((l) => l.code === r.locale) ? r.locale : DEFAULT_SETTINGS.locale,
@@ -1152,6 +1204,9 @@ function migrate(raw) {
1152
1204
  motifDensity: typeof r.motifDensity === "string" && MOTIFS.has(r.motifDensity) ? r.motifDensity : DEFAULT_SETTINGS.motifDensity,
1153
1205
  reducedMotion: typeof r.reducedMotion === "boolean" ? r.reducedMotion : DEFAULT_SETTINGS.reducedMotion
1154
1206
  };
1207
+ const layout = coerceDashboardLayout(r.dashboardLayout);
1208
+ if (layout) out.dashboardLayout = layout;
1209
+ return out;
1155
1210
  }
1156
1211
  function memoryPersistence() {
1157
1212
  let store;
@@ -1266,6 +1321,7 @@ function applySettings(s, el) {
1266
1321
  }
1267
1322
  export {
1268
1323
  CommandSender,
1324
+ DASHBOARD_LAYOUT_VERSION,
1269
1325
  DEFAULT_LOCALE,
1270
1326
  DEFAULT_SETTINGS,
1271
1327
  DEFAULT_THEME,
@@ -1296,6 +1352,7 @@ export {
1296
1352
  aggregateSubscriptions,
1297
1353
  applySettings,
1298
1354
  applyTheme,
1355
+ coerceDashboardLayout,
1299
1356
  deepMerge,
1300
1357
  deriveCandidates,
1301
1358
  deriveKlippyState,
@@ -1320,10 +1377,12 @@ export {
1320
1377
  parseNotifyEvent,
1321
1378
  pluralCategory,
1322
1379
  registerWidget,
1380
+ resolveDashboard,
1323
1381
  resolveMessage,
1324
1382
  resolveMoonrakerUrl,
1325
1383
  roamSettings,
1326
1384
  stamp,
1385
+ surfaceHint,
1327
1386
  themeToCssVars,
1328
1387
  themes,
1329
1388
  tier,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@filamind-app/core",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Shared foundation for the FilaMind suite — Moonraker client, reactive printer state, provenance-stamped values, design tokens, and the widget/plugin registry. Framework-agnostic TypeScript.",
5
5
  "license": "GPL-3.0-or-later",
6
6
  "repository": {