@stnd/launcher 1.2.0 → 1.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @stnd/launcher
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Auto-bumped @stnd/launcher due to modified code.
8
+
3
9
  ## 1.2.0
4
10
 
5
11
  ### Minor Changes
package/Launcher.svelte CHANGED
@@ -33,7 +33,14 @@
33
33
  // --- PROPS (all optional — the Launcher is fully self-booting) ---
34
34
  // Apps wire services via provide/inject (the Standard OS runtime store).
35
35
  // Props exist only for backward compat or one-off overrides.
36
- let { WelcomeView = null, load = null, hideLogin = false } = $props();
36
+ let {
37
+ WelcomeView = null,
38
+ FooterView = null,
39
+ footer = null,
40
+ showFooter = false,
41
+ load = null,
42
+ hideLogin = false,
43
+ } = $props();
37
44
 
38
45
  // --- STATE (Synced with Engine) ---
39
46
  let pState = $state({
@@ -380,6 +387,21 @@
380
387
  return null;
381
388
  });
382
389
 
390
+ // --- FOOTER RESOLUTION (Optional) ---
391
+ let ActiveFooterComponent = $derived.by(() => {
392
+ if (FooterView) return FooterView;
393
+ if (registryState.views["::footer"]) return registryState.views["::footer"];
394
+ return inject("launcher:footer", null);
395
+ });
396
+
397
+ let shouldRenderFooter = $derived.by(() => {
398
+ if (footer || ActiveFooterComponent) return true;
399
+ const show = showFooter || inject("launcher:showFooter", false);
400
+ if (!show) return false;
401
+ if (ActiveViewComponent && show !== "always") return false;
402
+ return true;
403
+ });
404
+
383
405
  // Determine view size from registry meta
384
406
  let activeViewSize = $derived.by(() => {
385
407
  // 1. Dynamic Override
@@ -639,6 +661,57 @@
639
661
  <div class="std-launcher-empty">Nothing came up.</div>
640
662
  {/if}
641
663
  </div>
664
+
665
+ <!-- Optional Launcher Footer -->
666
+ {#if shouldRenderFooter}
667
+ <footer class="std-launcher-footer">
668
+ {#if footer}
669
+ {@render footer({ currentUser, pState, close: launcherClose })}
670
+ {:else if ActiveFooterComponent}
671
+ <ActiveFooterComponent {currentUser} {pState} close={launcherClose} />
672
+ {:else}
673
+ <!-- Default Navigation Hints & Info -->
674
+ <div class="std-launcher-footer-left">
675
+ {#if currentUser && currentUser.isAuthenticated}
676
+ <div
677
+ class="std-launcher-user"
678
+ role="button"
679
+ tabindex="0"
680
+ onclick={() => {
681
+ if (registryState.views["::account-settings"]) {
682
+ launcherView("::account-settings");
683
+ } else if (registryState.views["::settings"]) {
684
+ launcherView("::settings");
685
+ } else {
686
+ launcherView("::my-space");
687
+ }
688
+ }}
689
+ onkeydown={(e) => {
690
+ if (e.key === "Enter" || e.key === " ") {
691
+ if (registryState.views["::account-settings"]) {
692
+ launcherView("::account-settings");
693
+ } else if (registryState.views["::settings"]) {
694
+ launcherView("::settings");
695
+ } else {
696
+ launcherView("::my-space");
697
+ }
698
+ }
699
+ }}
700
+ >
701
+ <span class="smaller muted">
702
+ {currentUser.name || currentUser.username || currentUser.email}
703
+ </span>
704
+ </div>
705
+ {/if}
706
+ </div>
707
+
708
+ <div class="std-launcher-hints">
709
+ <span><kbd>↑</kbd> <kbd>↓</kbd> navigate</span>
710
+ <span><kbd>esc</kbd> close</span>
711
+ </div>
712
+ {/if}
713
+ </footer>
714
+ {/if}
642
715
  </div>
643
716
  </div>
644
717
  {/if}
@@ -984,6 +1057,66 @@
984
1057
  font-size: var(--scale-d2);
985
1058
  }
986
1059
 
1060
+ /* Optional Footer */
1061
+ .std-launcher-footer {
1062
+ display: flex;
1063
+ justify-content: space-between;
1064
+ align-items: center;
1065
+ padding: var(--space-d4) var(--space-d2);
1066
+ min-height: 28px;
1067
+ font-size: var(--scale-d2, 0.75rem);
1068
+ color: var(--color-muted);
1069
+ border-top: var(--border);
1070
+ background-color: var(--color-surface-high);
1071
+ box-shadow: var(--shadow-raised);
1072
+ flex-shrink: 0;
1073
+ }
1074
+
1075
+ .std-launcher-footer-left {
1076
+ display: flex;
1077
+ align-items: center;
1078
+ gap: var(--space-d3);
1079
+ }
1080
+
1081
+ .std-launcher-user {
1082
+ display: flex;
1083
+ align-items: center;
1084
+ gap: var(--space-d4);
1085
+ color: var(--color-foreground-muted);
1086
+ padding: var(--space-d5) var(--space-d3);
1087
+ border-radius: var(--radius);
1088
+ transition: all var(--transition-fast);
1089
+ cursor: pointer;
1090
+ }
1091
+
1092
+ .std-launcher-user:hover {
1093
+ background: var(--color-surface-highest);
1094
+ color: var(--color-foreground);
1095
+ }
1096
+
1097
+ .std-launcher-user span {
1098
+ max-width: 150px;
1099
+ white-space: nowrap;
1100
+ overflow: hidden;
1101
+ text-overflow: ellipsis;
1102
+ }
1103
+
1104
+ .std-launcher-hints {
1105
+ display: flex;
1106
+ align-items: center;
1107
+ gap: var(--space-d1);
1108
+ font-size: var(--scale-d2, 0.75rem);
1109
+ }
1110
+
1111
+ .std-launcher-hints kbd {
1112
+ font-family: var(--font-mono, monospace);
1113
+ font-size: var(--scale-d3, 0.7rem);
1114
+ padding: 1px var(--space-d4);
1115
+ border-radius: var(--radius-sm, 3px);
1116
+ background: var(--color-surface-highest);
1117
+ border: var(--border);
1118
+ color: var(--color-foreground);
1119
+ }
987
1120
 
988
1121
  /* Mobile: Bottom Sheet */
989
1122
  @media (max-width: 640px) {
package/README.md CHANGED
@@ -65,12 +65,15 @@ Import and mount the `<Launcher />` component in your layout:
65
65
 
66
66
  ### Props
67
67
 
68
- | Prop | Type | Default | Description |
69
- | :------------ | :------------------------------- | :------ | :---------------------------------------- |
70
- | `load` | `string \| string[] \| function` | `null` | Filter specific commands/views to load. |
71
- | `userStore` | `readable` | `null` | Svelte store containing the current user. |
72
- | `searchNotes` | `function` | `null` | Async function to search app content. |
73
- | `WelcomeView` | `Component` | `null` | Custom component for the empty state. |
68
+ | Prop | Type | Default | Description |
69
+ | :------------ | :------------------------------- | :------ | :----------------------------------------------------- |
70
+ | `load` | `string \| string[] \| function` | `null` | Filter specific commands/views to load. |
71
+ | `userStore` | `readable` | `null` | Svelte store containing the current user. |
72
+ | `searchNotes` | `function` | `null` | Async function to search app content. |
73
+ | `WelcomeView` | `Component` | `null` | Custom component for the empty state. |
74
+ | `FooterView` | `Component` | `null` | Custom component for the optional launcher footer. |
75
+ | `footer` | `Snippet` | `null` | Svelte 5 snippet for custom footer content. |
76
+ | `showFooter` | `boolean \| "always"` | `false` | Enable built-in footer (navigation hints & info). |
74
77
 
75
78
  ## Core Concepts
76
79
 
package/launcher.ts CHANGED
@@ -117,11 +117,15 @@ export function configureLauncher(
117
117
  searchNotes?: (query: string) => Promise<LauncherResult[]>;
118
118
  userStore?: any;
119
119
  filter?: (trigger: string, meta: CommandMeta) => boolean;
120
+ footer?: any;
121
+ showFooter?: boolean | "always";
120
122
  } = {},
121
123
  ) {
122
124
  // Legacy compat: if callers still pass these, push them into the runtime store
123
125
  if (options.searchNotes) provide("search", options.searchNotes);
124
126
  if (options.userStore) provide("user", options.userStore);
127
+ if (options.footer) provide("launcher:footer", options.footer);
128
+ if (options.showFooter !== undefined) provide("launcher:showFooter", options.showFooter);
125
129
  if (options.filter) appFilter = options.filter;
126
130
  }
127
131
 
@@ -135,6 +139,8 @@ export function initLauncher(
135
139
  searchNotes?: (query: string) => Promise<LauncherResult[]>;
136
140
  userStore?: any;
137
141
  filter?: (trigger: string, meta: CommandMeta) => boolean;
142
+ footer?: any;
143
+ showFooter?: boolean | "always";
138
144
  } = {},
139
145
  ) {
140
146
  configureLauncher(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stnd/launcher",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },