@lapis-notes/workspace 0.2.1 → 0.3.1

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.
Files changed (39) hide show
  1. package/CHANGELOG.md +123 -12
  2. package/README.md +6 -2
  3. package/dist/WorkspaceShell.svelte +19 -2
  4. package/dist/WorkspaceShell.svelte.d.ts +3 -1
  5. package/dist/desktop/DesktopRecoveryBanner.css +46 -0
  6. package/dist/desktop/DesktopRecoveryBanner.svelte +70 -0
  7. package/dist/desktop/DesktopRecoveryBanner.svelte.d.ts +15 -0
  8. package/dist/desktop/DesktopVaultLauncher.svelte +278 -0
  9. package/dist/desktop/DesktopVaultLauncher.svelte.d.ts +17 -0
  10. package/dist/desktop/DesktopVaultLauncherDialogs.svelte +136 -0
  11. package/dist/desktop/DesktopVaultLauncherDialogs.svelte.d.ts +9 -0
  12. package/dist/desktop/DesktopWorkspaceStartup.css +8 -0
  13. package/dist/desktop/DesktopWorkspaceStartup.svelte +34 -0
  14. package/dist/desktop/DesktopWorkspaceStartup.svelte.d.ts +9 -0
  15. package/dist/desktop/desktop-vault-launcher-controls.css +142 -0
  16. package/dist/desktop/desktop-vault-launcher.css +181 -0
  17. package/dist/desktop/desktop-vault-launcher.d.ts +30 -0
  18. package/dist/desktop/desktop-vault-launcher.js +26 -0
  19. package/dist/desktop/desktop-vault-launcher.svelte.d.ts +36 -0
  20. package/dist/desktop/desktop-vault-launcher.svelte.js +140 -0
  21. package/dist/desktop/lapis-logo.svg +471 -0
  22. package/dist/file-conflicts/ConflictReview.svelte +153 -0
  23. package/dist/file-conflicts/ConflictReview.svelte.d.ts +8 -0
  24. package/dist/file-conflicts/ConflictReviewControls.svelte +66 -0
  25. package/dist/file-conflicts/ConflictReviewControls.svelte.d.ts +12 -0
  26. package/dist/file-conflicts/conflict-review-state.svelte.d.ts +22 -0
  27. package/dist/file-conflicts/conflict-review-state.svelte.js +59 -0
  28. package/dist/file-conflicts/conflict-review-view.d.ts +19 -0
  29. package/dist/file-conflicts/conflict-review-view.js +50 -0
  30. package/dist/file-conflicts/register-conflict-reviews.d.ts +7 -0
  31. package/dist/file-conflicts/register-conflict-reviews.js +132 -0
  32. package/dist/index.d.ts +6 -0
  33. package/dist/index.js +4 -0
  34. package/dist/plugin-management/CorePluginsSettingsPage.svelte +47 -16
  35. package/dist/plugin-management/PluginBrowser.svelte +16 -1
  36. package/dist/plugin-management/PluginRegistryToolbar.svelte.d.ts +1 -1
  37. package/dist/plugin-management/plugin-registry-controller.svelte.js +5 -1
  38. package/dist/plugin-management/register-plugin-management-settings.js +7 -6
  39. package/package.json +5 -4
package/CHANGELOG.md CHANGED
@@ -1,36 +1,147 @@
1
1
  # @lapis-notes/workspace
2
2
 
3
+ ## 0.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#44](https://github.com/lapismd/lapis-notes/pull/44)
8
+ [`f2373ba`](https://github.com/lapismd/lapis-notes/commit/f2373baf79db19267f3422895ce321ef1fa3c78d)
9
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Point package
10
+ documentation to the canonical specification and remove stale package-local
11
+ and pre-consolidation specification links.
12
+
13
+ - [#47](https://github.com/lapismd/lapis-notes/pull/47)
14
+ [`6f315ba`](https://github.com/lapismd/lapis-notes/commit/6f315baef2bd2f0bb4942927d4e19b9e0eb2a15d)
15
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Restore explicit
16
+ disk-version acceptance in the shared retained conflict review, including
17
+ preserving deletion through the captured guarded writer decision.
18
+ - Updated dependencies
19
+ [[`f2373ba`](https://github.com/lapismd/lapis-notes/commit/f2373baf79db19267f3422895ce321ef1fa3c78d)]:
20
+ - @lapis-notes/api@0.2.1
21
+ - @lapis-notes/ui@0.1.5
22
+
23
+ ## 0.3.0
24
+
25
+ ### Minor Changes
26
+
27
+ - [`535a6cc`](https://github.com/lapismd/lapis-notes/commit/535a6cc7b39f397cf8761a06db7b831386121928)
28
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Share the existing
29
+ desktop vault chooser and branded startup presentation across native hosts,
30
+ with injected profile storage and native actions.
31
+
32
+ - [`fc3ecdf`](https://github.com/lapismd/lapis-notes/commit/fc3ecdf374005e3c2e960af7d84709b92803537a)
33
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Share the retained
34
+ text-conflict presentation through the App-owned `bindFileConflictReviews`
35
+ binding: one reference-counted lease for WorkspaceShell mounts and early host
36
+ surfaces captured writer conflicts as Problems diagnostics and a three-way
37
+ MergeEditor review leaf whose saves and refreshes run through the writer-owned
38
+ captured actions (LN-WS-102–104).
39
+
40
+ ### Patch Changes
41
+
42
+ - [`4cdbb7b`](https://github.com/lapismd/lapis-notes/commit/4cdbb7b09f2875d413bf0e3fa054ed16345866a8)
43
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Expose the existing
44
+ desktop Safe Mode recovery presentation through shared, host-controlled
45
+ callbacks for Tauri.
46
+
47
+ - [`d8946b9`](https://github.com/lapismd/lapis-notes/commit/d8946b941569484b813eb0c1e8b1c5729fffc1a7)
48
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Add owned
49
+ diagnostic-resource navigation and themed recovery-banner actions. Accept
50
+ Design Core 0.5.1 alongside the existing 0.4 range.
51
+
52
+ - Require released Design Core 0.6 for the close-guard, plugin-flush and
53
+ centered panel contracts used by current source. Retain portable registry
54
+ dependency ranges for the shared application runtime.
55
+ - Updated dependencies
56
+ [[`bdd4923`](https://github.com/lapismd/lapis-notes/commit/bdd492357135de2cc0175fa42413709b2a38cd63),
57
+ [`86e85f6`](https://github.com/lapismd/lapis-notes/commit/86e85f6e814430c1755147c913eb91090a439bf4),
58
+ [`631aeec`](https://github.com/lapismd/lapis-notes/commit/631aeec539e2fb061dc80c5c3a30a248ee15c529),
59
+ [`fdee7e4`](https://github.com/lapismd/lapis-notes/commit/fdee7e4defde17a1009e16138d78c93c3ead8e2f),
60
+ [`a91d5e2`](https://github.com/lapismd/lapis-notes/commit/a91d5e2eb5ec037029f6a7a2df316a9bd9440af7),
61
+ [`fbba8d4`](https://github.com/lapismd/lapis-notes/commit/fbba8d48498c34939fec475e6bed67a682045820),
62
+ [`d8946b9`](https://github.com/lapismd/lapis-notes/commit/d8946b941569484b813eb0c1e8b1c5729fffc1a7),
63
+ [`ebf033a`](https://github.com/lapismd/lapis-notes/commit/ebf033af0fc0752be5ce5ffa7d55784163d291b4),
64
+ [`023259d`](https://github.com/lapismd/lapis-notes/commit/023259d96d4ff0414fec6427924ed917f89405a3),
65
+ [`63a5e7f`](https://github.com/lapismd/lapis-notes/commit/63a5e7fe40eea45762b5842b588c742076c7dd1d),
66
+ [`2bffe48`](https://github.com/lapismd/lapis-notes/commit/2bffe484b38761af197afe7ab9b0d7b3affb50d7),
67
+ [`86f6316`](https://github.com/lapismd/lapis-notes/commit/86f63166c3d5be648c595ef4e230ea6c4081bc48),
68
+ [`edf1d76`](https://github.com/lapismd/lapis-notes/commit/edf1d76f4f3aa0442c1d9153d812f29b7d14e5c2),
69
+ [`b3be91d`](https://github.com/lapismd/lapis-notes/commit/b3be91daecbd43187bacf8cfdc39f8311d700720),
70
+ [`5ef0a63`](https://github.com/lapismd/lapis-notes/commit/5ef0a6331c1b846b70357f990d919977a561112c),
71
+ [`bac3e5e`](https://github.com/lapismd/lapis-notes/commit/bac3e5eda6f467dbc86ec88c9f27a275f2b27953),
72
+ [`978b987`](https://github.com/lapismd/lapis-notes/commit/978b987ba4c95408a63e49fad11bc44bab020758),
73
+ [`ad4490c`](https://github.com/lapismd/lapis-notes/commit/ad4490ca1cd808002765582a17a5c81fa5335e0d),
74
+ [`74f544e`](https://github.com/lapismd/lapis-notes/commit/74f544e2aa8a967524c0c218f3db7e1a495a1a17),
75
+ [`55be8dc`](https://github.com/lapismd/lapis-notes/commit/55be8dce6f7b19b0e8036be86a3cf5305db25317),
76
+ [`501c50e`](https://github.com/lapismd/lapis-notes/commit/501c50e049264560e13e29c75a74c089922c7297),
77
+ [`98b333f`](https://github.com/lapismd/lapis-notes/commit/98b333f73ff5ba8d5f28c52e4e3a2ceaa5ade0fb),
78
+ [`535a6cc`](https://github.com/lapismd/lapis-notes/commit/535a6cc7b39f397cf8761a06db7b831386121928),
79
+ [`8a27bc3`](https://github.com/lapismd/lapis-notes/commit/8a27bc36a7a7fa81594e517908a6f5ed8ca77313),
80
+ [`109e208`](https://github.com/lapismd/lapis-notes/commit/109e208a63253dcc1a7abad3fd5a9b13c39d3def),
81
+ [`1e64589`](https://github.com/lapismd/lapis-notes/commit/1e6458922b486c07f36a5bc7d85d020b5805cd49),
82
+ [`f828eae`](https://github.com/lapismd/lapis-notes/commit/f828eaeba8411aa342d7ab4954393e84612af2f3)]:
83
+ - @lapis-notes/api@0.2.0
84
+ - @lapis-notes/ui@0.1.4
85
+
86
+ ## 0.2.2
87
+
88
+ ### Patch Changes
89
+
90
+ - [#36](https://github.com/lapismd/lapis-notes/pull/36)
91
+ [`17060a1`](https://github.com/lapismd/lapis-notes/commit/17060a1df152c9fc2302c93767cd7172868ff030)
92
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Consume published Design
93
+ Core 0.5.0 with prepared browser CSS and bundled font assets through normal
94
+ npm ranges.
95
+ - Updated dependencies
96
+ [[`17060a1`](https://github.com/lapismd/lapis-notes/commit/17060a1df152c9fc2302c93767cd7172868ff030)]:
97
+ - @lapis-notes/api@0.1.5
98
+
3
99
  ## 0.2.1
4
100
 
5
101
  ### Patch Changes
6
102
 
7
- - [#15](https://github.com/lapismd/lapis-notes/pull/15) [`57996c6`](https://github.com/lapismd/lapis-notes/commit/57996c6d26c1e7ed723df1291b415ee94d3a58e5) Thanks [@stevejuma](https://github.com/stevejuma)! - Retain selected plugin releases in navigation and persisted Plugins views so
8
- application links can open a specific version. Fail closed for unavailable
9
- versions and keep historical installation bound to the selected release.
103
+ - [#15](https://github.com/lapismd/lapis-notes/pull/15)
104
+ [`57996c6`](https://github.com/lapismd/lapis-notes/commit/57996c6d26c1e7ed723df1291b415ee94d3a58e5)
105
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Retain selected plugin
106
+ releases in navigation and persisted Plugins views so application links can
107
+ open a specific version. Fail closed for unavailable versions and keep
108
+ historical installation bound to the selected release.
10
109
 
11
110
  ## 0.2.0
12
111
 
13
112
  ### Minor Changes
14
113
 
15
- - [#4](https://github.com/lapismd/lapis-notes/pull/4) [`08a9e4b`](https://github.com/lapismd/lapis-notes/commit/08a9e4bc671c87484e1fbb7c3271b2aad3f176e5) Thanks [@stevejuma](https://github.com/stevejuma)! - Add the Plugins workspace browser with a category homepage, update listings,
16
- responsive filters, and verified install/update actions shared with Settings.
17
- Settings Browse and Updates now open shared plugin details directly. Details
18
- include a Back action, fixed navigation, themed media, image loading and failure
19
- states, release notes, download counts, and linked authors.
114
+ - [#4](https://github.com/lapismd/lapis-notes/pull/4)
115
+ [`08a9e4b`](https://github.com/lapismd/lapis-notes/commit/08a9e4bc671c87484e1fbb7c3271b2aad3f176e5)
116
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Add the Plugins workspace
117
+ browser with a category homepage, update listings, responsive filters, and
118
+ verified install/update actions shared with Settings. Settings Browse and
119
+ Updates now open shared plugin details directly. Details include a Back
120
+ action, fixed navigation, themed media, image loading and failure states,
121
+ release notes, download counts, and linked authors.
20
122
 
21
123
  ### Patch Changes
22
124
 
23
- - [#4](https://github.com/lapismd/lapis-notes/pull/4) [`c1ad3c4`](https://github.com/lapismd/lapis-notes/commit/c1ad3c4acee2eac1ae87c22f66e946d2e0db3bf2) Thanks [@stevejuma](https://github.com/stevejuma)! - Require published Design Core 0.3.2 for native window-controls spacing, bold settings headings, selected-tab indicators and mobile shell teardown. Consume the npm release through portable semver ranges and lockfile integrity.
125
+ - [#4](https://github.com/lapismd/lapis-notes/pull/4)
126
+ [`c1ad3c4`](https://github.com/lapismd/lapis-notes/commit/c1ad3c4acee2eac1ae87c22f66e946d2e0db3bf2)
127
+ Thanks [@stevejuma](https://github.com/stevejuma)! - Require published Design
128
+ Core 0.3.2 for native window-controls spacing, bold settings headings,
129
+ selected-tab indicators and mobile shell teardown. Consume the npm release
130
+ through portable semver ranges and lockfile integrity.
24
131
 
25
- - Updated dependencies [[`39f886d`](https://github.com/lapismd/lapis-notes/commit/39f886d017a28d916b3f6cac7d08b7723a04f45b), [`c1ad3c4`](https://github.com/lapismd/lapis-notes/commit/c1ad3c4acee2eac1ae87c22f66e946d2e0db3bf2), [`1e949fc`](https://github.com/lapismd/lapis-notes/commit/1e949fc9d51b795a50760e1146a9599602746c6b)]:
132
+ - Updated dependencies
133
+ [[`39f886d`](https://github.com/lapismd/lapis-notes/commit/39f886d017a28d916b3f6cac7d08b7723a04f45b),
134
+ [`c1ad3c4`](https://github.com/lapismd/lapis-notes/commit/c1ad3c4acee2eac1ae87c22f66e946d2e0db3bf2),
135
+ [`1e949fc`](https://github.com/lapismd/lapis-notes/commit/1e949fc9d51b795a50760e1146a9599602746c6b)]:
26
136
  - @lapis-notes/api@0.1.2
27
137
 
28
138
  ## 0.1.1
29
139
 
30
140
  ### Patch Changes
31
141
 
32
- - Support verified static V2 catalogs alongside Nostr sources, retain installation
33
- source provenance, and render themed registry images and structured release notes.
142
+ - Support verified static V2 catalogs alongside Nostr sources, retain
143
+ installation source provenance, and render themed registry images and
144
+ structured release notes.
34
145
  - Updated dependencies []:
35
146
  - @lapis-notes/api@0.1.1
36
147
 
package/README.md CHANGED
@@ -11,6 +11,10 @@ backend setup.
11
11
 
12
12
  - `WorkspaceShell`, the Svelte component used by hosts and demos.
13
13
  - `WorkspaceShellProps`, the public prop contract for typed Svelte consumers.
14
+ - `DesktopVaultLauncher` and `DesktopVaultLauncherHost`, the existing Deno
15
+ vault chooser with injected profile storage, appearance and native actions.
16
+ - `DesktopWorkspaceStartup`, the shared Lapis-branded Design Core startup
17
+ presentation. Both native hosts pass actual phase progress and recovery actions.
14
18
 
15
19
  ## Usage
16
20
 
@@ -32,5 +36,5 @@ pnpm --filter @lapis-notes/workspace test
32
36
 
33
37
  ## References
34
38
 
35
- - [Package spec](spec.md)
36
- - [Canonical spec](../../spec/src/20-packages/workspace/index.md)
39
+ - [Workspace contract](../../spec/src/workspace-shell.md)
40
+ - [Canonical spec](../../spec/src/workspace-shell.md)
@@ -1,10 +1,15 @@
1
1
  <script lang="ts" module>
2
2
  import type { App } from "@lapis-notes/api";
3
3
  import type { WorkspaceNavigation } from "@lapismd/design-core/workspace/app-shell";
4
- import type { WorkspaceRequestedDisplayMode } from "@lapismd/design-core/workspace/core";
4
+ import type {
5
+ WorkspaceRequestedDisplayMode,
6
+ WorkspacePopoutHost,
7
+ } from "@lapismd/design-core/workspace/core";
5
8
 
6
9
  export interface WorkspaceShellProps {
7
10
  app: App;
11
+ /** Optional host-owned popup creation policy. null disables detached windows. */
12
+ popoutHost?: WorkspacePopoutHost | null;
8
13
  displayMode?: WorkspaceRequestedDisplayMode;
9
14
  workspaceLabel?: string;
10
15
  workspaceNavigation?: WorkspaceNavigation;
@@ -14,14 +19,19 @@
14
19
 
15
20
  <script lang="ts">
16
21
  import { onMount, untrack } from "svelte";
17
- import { provideApplicationState } from "@lapis-notes/api";
22
+ import {
23
+ bindNativeEditingProblems,
24
+ provideApplicationState,
25
+ } from "@lapis-notes/api";
18
26
  import { getWorkspaceHostBinding } from "@lapis-notes/api/workspace-host";
19
27
  import { AppShell } from "@lapismd/design-core/workspace/app-shell";
28
+ import { bindFileConflictReviews } from "./file-conflicts/register-conflict-reviews";
20
29
  import "./workspace-shell.css";
21
30
 
22
31
  let {
23
32
  app,
24
33
  displayMode,
34
+ popoutHost,
25
35
  workspaceLabel = "Lapis Notes",
26
36
  workspaceNavigation,
27
37
  class: className = "",
@@ -39,6 +49,12 @@
39
49
  // AppShell.Root does not own startup. By the time this shell is mounted,
40
50
  // the consumer has loaded the vault and restored the Lapis workspace.
41
51
  void controller.start();
52
+ const stopNative = bindNativeEditingProblems(app);
53
+ const stopConflicts = bindFileConflictReviews(app);
54
+ return () => {
55
+ stopConflicts();
56
+ stopNative();
57
+ };
42
58
  });
43
59
  </script>
44
60
 
@@ -49,6 +65,7 @@
49
65
  >
50
66
  <AppShell.Root
51
67
  {controller}
68
+ {popoutHost}
52
69
  autoStart={false}
53
70
  {activateNewTabs}
54
71
  disposeOnDestroy={false}
@@ -1,8 +1,10 @@
1
1
  import type { App } from "@lapis-notes/api";
2
2
  import type { WorkspaceNavigation } from "@lapismd/design-core/workspace/app-shell";
3
- import type { WorkspaceRequestedDisplayMode } from "@lapismd/design-core/workspace/core";
3
+ import type { WorkspaceRequestedDisplayMode, WorkspacePopoutHost } from "@lapismd/design-core/workspace/core";
4
4
  export interface WorkspaceShellProps {
5
5
  app: App;
6
+ /** Optional host-owned popup creation policy. null disables detached windows. */
7
+ popoutHost?: WorkspacePopoutHost | null;
6
8
  displayMode?: WorkspaceRequestedDisplayMode;
7
9
  workspaceLabel?: string;
8
10
  workspaceNavigation?: WorkspaceNavigation;
@@ -0,0 +1,46 @@
1
+ .desktop-recovery-banner {
2
+ position: absolute;
3
+ z-index: 80;
4
+ inset-block-end: 0.75rem;
5
+ inset-inline-start: 50%;
6
+ display: flex;
7
+ width: min(56rem, calc(100% - 2rem));
8
+ box-sizing: border-box;
9
+ align-items: center;
10
+ gap: 1rem;
11
+ padding: 0.75rem 1rem;
12
+ border: 1px solid var(--ui-workspace-border);
13
+ border-radius: var(--ui-radius, 0.625rem);
14
+ color: var(--ui-workspace-foreground);
15
+ background: var(--ui-workspace-background, var(--background));
16
+ box-shadow: var(--ui-workspace-shadow-lg, 0 12px 30px rgb(0 0 0 / 16%));
17
+ translate: -50% 0;
18
+ }
19
+ .desktop-recovery-banner__message {
20
+ display: flex;
21
+ min-width: 0;
22
+ flex: 1;
23
+ flex-direction: column;
24
+ gap: 0.125rem;
25
+ font-size: 0.75rem;
26
+ color: var(--ui-workspace-muted-foreground);
27
+ }
28
+ .desktop-recovery-banner__message strong {
29
+ font-size: 0.875rem;
30
+ color: var(--ui-workspace-foreground);
31
+ }
32
+ .desktop-recovery-banner__actions {
33
+ display: flex;
34
+ flex-wrap: wrap;
35
+ justify-content: flex-end;
36
+ gap: 0.5rem;
37
+ }
38
+ @media (width < 48rem) {
39
+ .desktop-recovery-banner {
40
+ align-items: stretch;
41
+ flex-direction: column;
42
+ }
43
+ .desktop-recovery-banner__actions {
44
+ justify-content: flex-start;
45
+ }
46
+ }
@@ -0,0 +1,70 @@
1
+ <script lang="ts">
2
+ import DatabaseIcon from "@lucide/svelte/icons/database";
3
+ import SearchIcon from "@lucide/svelte/icons/search";
4
+ import RotateCcwIcon from "@lucide/svelte/icons/rotate-ccw";
5
+ import * as Button from "@lapismd/design-core/shadcn/button";
6
+ import "./DesktopRecoveryBanner.css";
7
+
8
+ let {
9
+ reasons = [],
10
+ lastFailure = "",
11
+ message = "",
12
+ metadataBusy = false,
13
+ searchBusy = false,
14
+ disabled = false,
15
+ onRebuildMetadata,
16
+ onRebuildSearch,
17
+ onRestart,
18
+ }: {
19
+ reasons?: readonly string[];
20
+ lastFailure?: string;
21
+ message?: string;
22
+ metadataBusy?: boolean;
23
+ searchBusy?: boolean;
24
+ disabled?: boolean;
25
+ onRebuildMetadata(): Promise<void>;
26
+ onRebuildSearch(): Promise<void>;
27
+ onRestart(): Promise<void>;
28
+ } = $props();
29
+ </script>
30
+
31
+ <div
32
+ class="desktop-recovery-banner"
33
+ role="status"
34
+ aria-label="Safe Mode"
35
+ data-ui-component="desktop-recovery-banner"
36
+ data-ui-part="root"
37
+ data-desktop-drag-region="false"
38
+ >
39
+ <div class="desktop-recovery-banner__message">
40
+ <strong>Safe Mode is active</strong>
41
+ <span
42
+ >{reasons.length
43
+ ? reasons.join(". ")
44
+ : "Startup recovery restrictions are active for this session."}</span
45
+ >
46
+ {#if lastFailure}<span>Last failure: {lastFailure}</span>{/if}
47
+ {#if message}<span>{message}</span>{/if}
48
+ </div>
49
+ <div class="desktop-recovery-banner__actions">
50
+ <Button.Root
51
+ variant="outline"
52
+ disabled={disabled || metadataBusy}
53
+ onclick={onRebuildMetadata}
54
+ >
55
+ <DatabaseIcon aria-hidden="true" />
56
+ {metadataBusy ? "Rebuilding metadata…" : "Rebuild metadata"}
57
+ </Button.Root>
58
+ <Button.Root
59
+ variant="outline"
60
+ disabled={disabled || searchBusy}
61
+ onclick={onRebuildSearch}
62
+ >
63
+ <SearchIcon aria-hidden="true" />
64
+ {searchBusy ? "Rebuilding Search…" : "Rebuild Search"}
65
+ </Button.Root>
66
+ <Button.Root {disabled} onclick={onRestart}>
67
+ <RotateCcwIcon aria-hidden="true" />Restart normally
68
+ </Button.Root>
69
+ </div>
70
+ </div>
@@ -0,0 +1,15 @@
1
+ import "./DesktopRecoveryBanner.css";
2
+ type $$ComponentProps = {
3
+ reasons?: readonly string[];
4
+ lastFailure?: string;
5
+ message?: string;
6
+ metadataBusy?: boolean;
7
+ searchBusy?: boolean;
8
+ disabled?: boolean;
9
+ onRebuildMetadata(): Promise<void>;
10
+ onRebuildSearch(): Promise<void>;
11
+ onRestart(): Promise<void>;
12
+ };
13
+ declare const DesktopRecoveryBanner: import("svelte").Component<$$ComponentProps, {}, "">;
14
+ type DesktopRecoveryBanner = ReturnType<typeof DesktopRecoveryBanner>;
15
+ export default DesktopRecoveryBanner;
@@ -0,0 +1,278 @@
1
+ <script lang="ts">
2
+ import Archive from "@lucide/svelte/icons/archive";
3
+ import Copy from "@lucide/svelte/icons/copy";
4
+ import Ellipsis from "@lucide/svelte/icons/ellipsis";
5
+ import FolderInput from "@lucide/svelte/icons/folder-input";
6
+ import FolderOpen from "@lucide/svelte/icons/folder-open";
7
+ import FolderPlus from "@lucide/svelte/icons/folder-plus";
8
+ import PenLine from "@lucide/svelte/icons/pen-line";
9
+ import Settings from "@lucide/svelte/icons/settings";
10
+ import X from "@lucide/svelte/icons/x";
11
+ import type { VaultProfile } from "@lapis-notes/api";
12
+ import * as Button from "@lapismd/design-core/shadcn/button";
13
+ import * as Card from "@lapismd/design-core/shadcn/card";
14
+ import * as DropdownMenu from "@lapismd/design-core/shadcn/dropdown-menu";
15
+ import { onMount, untrack } from "svelte";
16
+ import { DesktopVaultLauncherState } from "./desktop-vault-launcher.svelte";
17
+ import {
18
+ getVaultProfileLocation,
19
+ getVaultProfileRootPath,
20
+ type LauncherStatus,
21
+ type DesktopVaultLauncherHost,
22
+ } from "./desktop-vault-launcher";
23
+ import DesktopVaultLauncherDialogs from "./DesktopVaultLauncherDialogs.svelte";
24
+ import lapisLogo from "./lapis-logo.svg";
25
+ import "./desktop-vault-launcher.css";
26
+ import "./desktop-vault-launcher-controls.css";
27
+ let {
28
+ status,
29
+ errorMessage = "",
30
+ canReturn = false,
31
+ host,
32
+ onCreate,
33
+ onOpen,
34
+ onOpenRecent,
35
+ onClose,
36
+ }: {
37
+ status: LauncherStatus;
38
+ errorMessage?: string;
39
+ canReturn?: boolean;
40
+ host: DesktopVaultLauncherHost;
41
+ onCreate(): Promise<void>;
42
+ onOpen(): Promise<void>;
43
+ onOpenRecent(profile: VaultProfile): Promise<void>;
44
+ onClose?(): void;
45
+ } = $props();
46
+ const model = new DesktopVaultLauncherState(untrack(() => host));
47
+ const busy = $derived(status === "loading" || status === "opening");
48
+ const visibleError = $derived(errorMessage || model.actionError);
49
+ const actionClass = "desktop-launcher__action";
50
+ const outlineActionClass = `${actionClass} desktop-launcher__outline`;
51
+ onMount(() => {
52
+ void model.refresh();
53
+ });
54
+ </script>
55
+
56
+ <section
57
+ class="desktop-launcher"
58
+ data-ui-component="desktop-vault-launcher"
59
+ data-desktop-vault-launcher
60
+ data-desktop-drag-region
61
+ aria-label="Vaults"
62
+ aria-busy={busy}
63
+ >
64
+ <div
65
+ class="desktop-launcher__content"
66
+ data-desktop-vault-launcher-content
67
+ data-desktop-drag-region="false"
68
+ >
69
+ <div class="desktop-launcher__header">
70
+ <div class="desktop-launcher__brand">
71
+ <img src={lapisLogo} alt="" class="desktop-launcher__logo" />
72
+ <div class="desktop-launcher__copy">
73
+ <div class="desktop-launcher__eyebrow">Lapis Notes</div>
74
+ <h1 class="desktop-launcher__heading">
75
+ {model.recentVaults.length === 0
76
+ ? "Create a vault"
77
+ : "Open a vault"}
78
+ </h1>
79
+ <p class="desktop-launcher__description">
80
+ {#if model.recentVaults.length === 0}
81
+ Start a new vault on this Mac, or open an existing folder if you
82
+ already have one.
83
+ {:else}
84
+ Jump back into a recent vault or create a new one on this Mac.
85
+ {/if}
86
+ </p>
87
+ </div>
88
+ </div>
89
+ <div class="desktop-launcher__header-actions">
90
+ <Button.Root
91
+ variant="link"
92
+ size="sm"
93
+ class="desktop-launcher__settings-trigger"
94
+ onclick={() => (model.settingsOpen = true)}
95
+ >
96
+ <Settings class="desktop-launcher__icon" />
97
+ Settings
98
+ </Button.Root>
99
+ {#if canReturn}
100
+ <Button.Root
101
+ type="button"
102
+ variant="secondary"
103
+ size="icon-lg"
104
+ class="desktop-launcher__close"
105
+ aria-label="Return to previous vault"
106
+ onclick={() => onClose?.()}
107
+ >
108
+ <X class="desktop-launcher__large-icon" />
109
+ </Button.Root>
110
+ {/if}
111
+ </div>
112
+ </div>
113
+
114
+ {#if visibleError}
115
+ <div class="desktop-launcher__error" role="alert">
116
+ {visibleError}
117
+ </div>
118
+ {:else if model.actionMessage}
119
+ <div class="desktop-launcher__message" role="status">
120
+ {model.actionMessage}
121
+ </div>
122
+ {/if}
123
+
124
+ <div class="desktop-launcher__columns">
125
+ <Card.Root class="desktop-launcher__actions desktop-launcher__card">
126
+ <Card.Header class="desktop-launcher__card-header">
127
+ <Card.Title class="desktop-launcher__card-title">
128
+ {model.recentVaults.length === 0
129
+ ? "New Desktop Vault"
130
+ : "Desktop Vaults"}
131
+ </Card.Title>
132
+ <Card.Description>
133
+ {model.recentVaults.length === 0
134
+ ? "Create a new vault directory first, or open an existing folder."
135
+ : "Choose an existing folder or create a new vault directory."}
136
+ </Card.Description>
137
+ </Card.Header>
138
+ <Card.Content class="desktop-launcher__action-list">
139
+ <Button.Root
140
+ variant={model.recentVaults.length === 0 ? "default" : "outline"}
141
+ class={model.recentVaults.length === 0
142
+ ? actionClass
143
+ : outlineActionClass}
144
+ disabled={busy}
145
+ onclick={() => void model.invoke(onCreate)}
146
+ >
147
+ <FolderPlus class="desktop-launcher__large-icon" />
148
+ <span class="desktop-launcher__action-copy">
149
+ <span class="desktop-launcher__label">Create New Vault</span>
150
+ <span class="desktop-launcher__hint">
151
+ Pick a new folder location and open it immediately.
152
+ </span>
153
+ </span>
154
+ </Button.Root>
155
+ <Button.Root
156
+ variant={model.recentVaults.length === 0 ? "outline" : "default"}
157
+ class={model.recentVaults.length === 0
158
+ ? outlineActionClass
159
+ : actionClass}
160
+ disabled={busy}
161
+ onclick={() => void model.invoke(onOpen)}
162
+ >
163
+ <FolderOpen class="desktop-launcher__large-icon" />
164
+ <span class="desktop-launcher__action-copy">
165
+ <span class="desktop-launcher__label">Open Vault</span>
166
+ <span class="desktop-launcher__hint">
167
+ Choose a folder you have already been using as a vault.
168
+ </span>
169
+ </span>
170
+ </Button.Root>
171
+ </Card.Content>
172
+ </Card.Root>
173
+
174
+ <Card.Root class="desktop-launcher__recents desktop-launcher__card">
175
+ <Card.Header class="desktop-launcher__card-header">
176
+ <div class="desktop-launcher__recents-header">
177
+ <div>
178
+ <Card.Title class="desktop-launcher__card-title"
179
+ >Recent Projects</Card.Title
180
+ >
181
+ <Card.Description
182
+ >Recently opened vaults stored on this device.</Card.Description
183
+ >
184
+ </div>
185
+ {#if model.recentVaults.length > 0}
186
+ <Button.Root
187
+ variant="link"
188
+ size="sm"
189
+ class="desktop-launcher__link"
190
+ onclick={() => (model.recentVaultDialogOpen = true)}
191
+ >
192
+ View all
193
+ </Button.Root>
194
+ {/if}
195
+ </div>
196
+ </Card.Header>
197
+ <Card.Content class="desktop-launcher__recent-list">
198
+ {#if model.inlineRecentVaults.length > 0}
199
+ {#each model.inlineRecentVaults as profile (profile.id)}
200
+ <div class="desktop-launcher__recent">
201
+ <button
202
+ class="desktop-launcher__recent-open"
203
+ data-ui-part="recent-open"
204
+ type="button"
205
+ disabled={busy || model.activeActionId === profile.id}
206
+ onclick={() => void model.invoke(() => onOpenRecent(profile))}
207
+ >
208
+ <FolderOpen class="desktop-launcher__recent-icon" />
209
+ <span class="desktop-launcher__recent-copy">
210
+ <span class="desktop-launcher__recent-name"
211
+ >{profile.name}</span
212
+ >
213
+ <span class="desktop-launcher__location"
214
+ >{getVaultProfileLocation(profile)}</span
215
+ >
216
+ </span>
217
+ </button>
218
+ {#if host.copyId || host.rename || host.move || host.reveal || host.remove}
219
+ <DropdownMenu.Root>
220
+ <DropdownMenu.Trigger
221
+ class="desktop-launcher__recent-menu"
222
+ aria-label={`Open actions for ${profile.name}`}
223
+ title={`Open actions for ${profile.name}`}
224
+ disabled={busy || model.activeActionId === profile.id}
225
+ >
226
+ <Ellipsis class="desktop-launcher__icon" />
227
+ </DropdownMenu.Trigger>
228
+ <DropdownMenu.Content align="end" side="bottom">
229
+ {#if host.copyId}<DropdownMenu.Item
230
+ onclick={() => void model.copyVaultId(profile)}
231
+ >
232
+ <Copy class="desktop-launcher__icon" />
233
+ Copy vault ID
234
+ </DropdownMenu.Item>{/if}
235
+ {#if host.rename}<DropdownMenu.Item
236
+ onclick={() => model.openRenameDialog(profile)}
237
+ >
238
+ <PenLine class="desktop-launcher__icon" />
239
+ Rename vault...
240
+ </DropdownMenu.Item>{/if}
241
+ {#if host.move}<DropdownMenu.Item
242
+ disabled={!getVaultProfileRootPath(profile)}
243
+ onclick={() => void model.moveRecentVault(profile)}
244
+ >
245
+ <FolderInput class="desktop-launcher__icon" />
246
+ Move vault...
247
+ </DropdownMenu.Item>{/if}
248
+ {#if host.reveal}<DropdownMenu.Item
249
+ disabled={!getVaultProfileRootPath(profile)}
250
+ onclick={() => void model.revealRecentVault(profile)}
251
+ >
252
+ <FolderOpen class="desktop-launcher__icon" />
253
+ {host.revealLabel ?? "Reveal vault in file manager"}
254
+ </DropdownMenu.Item>{/if}
255
+ {#if host.remove}<DropdownMenu.Separator />{/if}
256
+ {#if host.remove}<DropdownMenu.Item
257
+ onclick={() => void model.removeRecentVault(profile)}
258
+ >
259
+ <Archive class="desktop-launcher__icon" />
260
+ Remove from list
261
+ </DropdownMenu.Item>{/if}
262
+ </DropdownMenu.Content>
263
+ </DropdownMenu.Root>
264
+ {/if}
265
+ </div>
266
+ {/each}
267
+ {:else}
268
+ <div class="desktop-launcher__empty">
269
+ Your recent vaults will appear here after you create or open one.
270
+ </div>
271
+ {/if}
272
+ </Card.Content>
273
+ </Card.Root>
274
+ </div>
275
+ </div>
276
+ </section>
277
+
278
+ <DesktopVaultLauncherDialogs {model} {onOpenRecent} />