@lapis-notes/workspace 0.2.1 → 0.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 +103 -12
- package/README.md +4 -0
- package/dist/WorkspaceShell.svelte +19 -2
- package/dist/WorkspaceShell.svelte.d.ts +3 -1
- package/dist/desktop/DesktopRecoveryBanner.css +46 -0
- package/dist/desktop/DesktopRecoveryBanner.svelte +70 -0
- package/dist/desktop/DesktopRecoveryBanner.svelte.d.ts +15 -0
- package/dist/desktop/DesktopVaultLauncher.svelte +278 -0
- package/dist/desktop/DesktopVaultLauncher.svelte.d.ts +17 -0
- package/dist/desktop/DesktopVaultLauncherDialogs.svelte +136 -0
- package/dist/desktop/DesktopVaultLauncherDialogs.svelte.d.ts +9 -0
- package/dist/desktop/DesktopWorkspaceStartup.css +8 -0
- package/dist/desktop/DesktopWorkspaceStartup.svelte +34 -0
- package/dist/desktop/DesktopWorkspaceStartup.svelte.d.ts +9 -0
- package/dist/desktop/desktop-vault-launcher-controls.css +142 -0
- package/dist/desktop/desktop-vault-launcher.css +181 -0
- package/dist/desktop/desktop-vault-launcher.d.ts +30 -0
- package/dist/desktop/desktop-vault-launcher.js +26 -0
- package/dist/desktop/desktop-vault-launcher.svelte.d.ts +36 -0
- package/dist/desktop/desktop-vault-launcher.svelte.js +140 -0
- package/dist/desktop/lapis-logo.svg +471 -0
- package/dist/file-conflicts/ConflictReview.svelte +145 -0
- package/dist/file-conflicts/ConflictReview.svelte.d.ts +8 -0
- package/dist/file-conflicts/ConflictReviewControls.svelte +66 -0
- package/dist/file-conflicts/ConflictReviewControls.svelte.d.ts +12 -0
- package/dist/file-conflicts/conflict-review-state.svelte.d.ts +22 -0
- package/dist/file-conflicts/conflict-review-state.svelte.js +59 -0
- package/dist/file-conflicts/conflict-review-view.d.ts +19 -0
- package/dist/file-conflicts/conflict-review-view.js +50 -0
- package/dist/file-conflicts/register-conflict-reviews.d.ts +7 -0
- package/dist/file-conflicts/register-conflict-reviews.js +132 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +4 -0
- package/dist/plugin-management/CorePluginsSettingsPage.svelte +47 -16
- package/dist/plugin-management/PluginBrowser.svelte +16 -1
- package/dist/plugin-management/PluginRegistryToolbar.svelte.d.ts +1 -1
- package/dist/plugin-management/plugin-registry-controller.svelte.js +5 -1
- package/dist/plugin-management/register-plugin-management-settings.js +7 -6
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,36 +1,127 @@
|
|
|
1
1
|
# @lapis-notes/workspace
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`535a6cc`](https://github.com/lapismd/lapis-notes/commit/535a6cc7b39f397cf8761a06db7b831386121928)
|
|
8
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Share the existing
|
|
9
|
+
desktop vault chooser and branded startup presentation across native hosts,
|
|
10
|
+
with injected profile storage and native actions.
|
|
11
|
+
|
|
12
|
+
- [`fc3ecdf`](https://github.com/lapismd/lapis-notes/commit/fc3ecdf374005e3c2e960af7d84709b92803537a)
|
|
13
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Share the retained
|
|
14
|
+
text-conflict presentation through the App-owned `bindFileConflictReviews`
|
|
15
|
+
binding: one reference-counted lease for WorkspaceShell mounts and early host
|
|
16
|
+
surfaces captured writer conflicts as Problems diagnostics and a three-way
|
|
17
|
+
MergeEditor review leaf whose saves and refreshes run through the writer-owned
|
|
18
|
+
captured actions (LN-WS-102–104).
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- [`4cdbb7b`](https://github.com/lapismd/lapis-notes/commit/4cdbb7b09f2875d413bf0e3fa054ed16345866a8)
|
|
23
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Expose the existing
|
|
24
|
+
desktop Safe Mode recovery presentation through shared, host-controlled
|
|
25
|
+
callbacks for Tauri.
|
|
26
|
+
|
|
27
|
+
- [`d8946b9`](https://github.com/lapismd/lapis-notes/commit/d8946b941569484b813eb0c1e8b1c5729fffc1a7)
|
|
28
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Add owned
|
|
29
|
+
diagnostic-resource navigation and themed recovery-banner actions. Accept
|
|
30
|
+
Design Core 0.5.1 alongside the existing 0.4 range.
|
|
31
|
+
|
|
32
|
+
- Require released Design Core 0.6 for the close-guard, plugin-flush and
|
|
33
|
+
centered panel contracts used by current source. Retain portable registry
|
|
34
|
+
dependency ranges for the shared application runtime.
|
|
35
|
+
- Updated dependencies
|
|
36
|
+
[[`bdd4923`](https://github.com/lapismd/lapis-notes/commit/bdd492357135de2cc0175fa42413709b2a38cd63),
|
|
37
|
+
[`86e85f6`](https://github.com/lapismd/lapis-notes/commit/86e85f6e814430c1755147c913eb91090a439bf4),
|
|
38
|
+
[`631aeec`](https://github.com/lapismd/lapis-notes/commit/631aeec539e2fb061dc80c5c3a30a248ee15c529),
|
|
39
|
+
[`fdee7e4`](https://github.com/lapismd/lapis-notes/commit/fdee7e4defde17a1009e16138d78c93c3ead8e2f),
|
|
40
|
+
[`a91d5e2`](https://github.com/lapismd/lapis-notes/commit/a91d5e2eb5ec037029f6a7a2df316a9bd9440af7),
|
|
41
|
+
[`fbba8d4`](https://github.com/lapismd/lapis-notes/commit/fbba8d48498c34939fec475e6bed67a682045820),
|
|
42
|
+
[`d8946b9`](https://github.com/lapismd/lapis-notes/commit/d8946b941569484b813eb0c1e8b1c5729fffc1a7),
|
|
43
|
+
[`ebf033a`](https://github.com/lapismd/lapis-notes/commit/ebf033af0fc0752be5ce5ffa7d55784163d291b4),
|
|
44
|
+
[`023259d`](https://github.com/lapismd/lapis-notes/commit/023259d96d4ff0414fec6427924ed917f89405a3),
|
|
45
|
+
[`63a5e7f`](https://github.com/lapismd/lapis-notes/commit/63a5e7fe40eea45762b5842b588c742076c7dd1d),
|
|
46
|
+
[`2bffe48`](https://github.com/lapismd/lapis-notes/commit/2bffe484b38761af197afe7ab9b0d7b3affb50d7),
|
|
47
|
+
[`86f6316`](https://github.com/lapismd/lapis-notes/commit/86f63166c3d5be648c595ef4e230ea6c4081bc48),
|
|
48
|
+
[`edf1d76`](https://github.com/lapismd/lapis-notes/commit/edf1d76f4f3aa0442c1d9153d812f29b7d14e5c2),
|
|
49
|
+
[`b3be91d`](https://github.com/lapismd/lapis-notes/commit/b3be91daecbd43187bacf8cfdc39f8311d700720),
|
|
50
|
+
[`5ef0a63`](https://github.com/lapismd/lapis-notes/commit/5ef0a6331c1b846b70357f990d919977a561112c),
|
|
51
|
+
[`bac3e5e`](https://github.com/lapismd/lapis-notes/commit/bac3e5eda6f467dbc86ec88c9f27a275f2b27953),
|
|
52
|
+
[`978b987`](https://github.com/lapismd/lapis-notes/commit/978b987ba4c95408a63e49fad11bc44bab020758),
|
|
53
|
+
[`ad4490c`](https://github.com/lapismd/lapis-notes/commit/ad4490ca1cd808002765582a17a5c81fa5335e0d),
|
|
54
|
+
[`74f544e`](https://github.com/lapismd/lapis-notes/commit/74f544e2aa8a967524c0c218f3db7e1a495a1a17),
|
|
55
|
+
[`55be8dc`](https://github.com/lapismd/lapis-notes/commit/55be8dce6f7b19b0e8036be86a3cf5305db25317),
|
|
56
|
+
[`501c50e`](https://github.com/lapismd/lapis-notes/commit/501c50e049264560e13e29c75a74c089922c7297),
|
|
57
|
+
[`98b333f`](https://github.com/lapismd/lapis-notes/commit/98b333f73ff5ba8d5f28c52e4e3a2ceaa5ade0fb),
|
|
58
|
+
[`535a6cc`](https://github.com/lapismd/lapis-notes/commit/535a6cc7b39f397cf8761a06db7b831386121928),
|
|
59
|
+
[`8a27bc3`](https://github.com/lapismd/lapis-notes/commit/8a27bc36a7a7fa81594e517908a6f5ed8ca77313),
|
|
60
|
+
[`109e208`](https://github.com/lapismd/lapis-notes/commit/109e208a63253dcc1a7abad3fd5a9b13c39d3def),
|
|
61
|
+
[`1e64589`](https://github.com/lapismd/lapis-notes/commit/1e6458922b486c07f36a5bc7d85d020b5805cd49),
|
|
62
|
+
[`f828eae`](https://github.com/lapismd/lapis-notes/commit/f828eaeba8411aa342d7ab4954393e84612af2f3)]:
|
|
63
|
+
- @lapis-notes/api@0.2.0
|
|
64
|
+
- @lapis-notes/ui@0.1.4
|
|
65
|
+
|
|
66
|
+
## 0.2.2
|
|
67
|
+
|
|
68
|
+
### Patch Changes
|
|
69
|
+
|
|
70
|
+
- [#36](https://github.com/lapismd/lapis-notes/pull/36)
|
|
71
|
+
[`17060a1`](https://github.com/lapismd/lapis-notes/commit/17060a1df152c9fc2302c93767cd7172868ff030)
|
|
72
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Consume published Design
|
|
73
|
+
Core 0.5.0 with prepared browser CSS and bundled font assets through normal
|
|
74
|
+
npm ranges.
|
|
75
|
+
- Updated dependencies
|
|
76
|
+
[[`17060a1`](https://github.com/lapismd/lapis-notes/commit/17060a1df152c9fc2302c93767cd7172868ff030)]:
|
|
77
|
+
- @lapis-notes/api@0.1.5
|
|
78
|
+
|
|
3
79
|
## 0.2.1
|
|
4
80
|
|
|
5
81
|
### Patch Changes
|
|
6
82
|
|
|
7
|
-
- [#15](https://github.com/lapismd/lapis-notes/pull/15)
|
|
8
|
-
|
|
9
|
-
|
|
83
|
+
- [#15](https://github.com/lapismd/lapis-notes/pull/15)
|
|
84
|
+
[`57996c6`](https://github.com/lapismd/lapis-notes/commit/57996c6d26c1e7ed723df1291b415ee94d3a58e5)
|
|
85
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Retain selected plugin
|
|
86
|
+
releases in navigation and persisted Plugins views so application links can
|
|
87
|
+
open a specific version. Fail closed for unavailable versions and keep
|
|
88
|
+
historical installation bound to the selected release.
|
|
10
89
|
|
|
11
90
|
## 0.2.0
|
|
12
91
|
|
|
13
92
|
### Minor Changes
|
|
14
93
|
|
|
15
|
-
- [#4](https://github.com/lapismd/lapis-notes/pull/4)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
94
|
+
- [#4](https://github.com/lapismd/lapis-notes/pull/4)
|
|
95
|
+
[`08a9e4b`](https://github.com/lapismd/lapis-notes/commit/08a9e4bc671c87484e1fbb7c3271b2aad3f176e5)
|
|
96
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Add the Plugins workspace
|
|
97
|
+
browser with a category homepage, update listings, responsive filters, and
|
|
98
|
+
verified install/update actions shared with Settings. Settings Browse and
|
|
99
|
+
Updates now open shared plugin details directly. Details include a Back
|
|
100
|
+
action, fixed navigation, themed media, image loading and failure states,
|
|
101
|
+
release notes, download counts, and linked authors.
|
|
20
102
|
|
|
21
103
|
### Patch Changes
|
|
22
104
|
|
|
23
|
-
- [#4](https://github.com/lapismd/lapis-notes/pull/4)
|
|
105
|
+
- [#4](https://github.com/lapismd/lapis-notes/pull/4)
|
|
106
|
+
[`c1ad3c4`](https://github.com/lapismd/lapis-notes/commit/c1ad3c4acee2eac1ae87c22f66e946d2e0db3bf2)
|
|
107
|
+
Thanks [@stevejuma](https://github.com/stevejuma)! - Require published Design
|
|
108
|
+
Core 0.3.2 for native window-controls spacing, bold settings headings,
|
|
109
|
+
selected-tab indicators and mobile shell teardown. Consume the npm release
|
|
110
|
+
through portable semver ranges and lockfile integrity.
|
|
24
111
|
|
|
25
|
-
- Updated dependencies
|
|
112
|
+
- Updated dependencies
|
|
113
|
+
[[`39f886d`](https://github.com/lapismd/lapis-notes/commit/39f886d017a28d916b3f6cac7d08b7723a04f45b),
|
|
114
|
+
[`c1ad3c4`](https://github.com/lapismd/lapis-notes/commit/c1ad3c4acee2eac1ae87c22f66e946d2e0db3bf2),
|
|
115
|
+
[`1e949fc`](https://github.com/lapismd/lapis-notes/commit/1e949fc9d51b795a50760e1146a9599602746c6b)]:
|
|
26
116
|
- @lapis-notes/api@0.1.2
|
|
27
117
|
|
|
28
118
|
## 0.1.1
|
|
29
119
|
|
|
30
120
|
### Patch Changes
|
|
31
121
|
|
|
32
|
-
- Support verified static V2 catalogs alongside Nostr sources, retain
|
|
33
|
-
source provenance, and render themed registry images and
|
|
122
|
+
- Support verified static V2 catalogs alongside Nostr sources, retain
|
|
123
|
+
installation source provenance, and render themed registry images and
|
|
124
|
+
structured release notes.
|
|
34
125
|
- Updated dependencies []:
|
|
35
126
|
- @lapis-notes/api@0.1.1
|
|
36
127
|
|
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
|
|
|
@@ -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 {
|
|
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 {
|
|
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} />
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { VaultProfile } from "@lapis-notes/api";
|
|
2
|
+
import { type LauncherStatus, type DesktopVaultLauncherHost } from "./desktop-vault-launcher";
|
|
3
|
+
import "./desktop-vault-launcher.css";
|
|
4
|
+
import "./desktop-vault-launcher-controls.css";
|
|
5
|
+
type $$ComponentProps = {
|
|
6
|
+
status: LauncherStatus;
|
|
7
|
+
errorMessage?: string;
|
|
8
|
+
canReturn?: boolean;
|
|
9
|
+
host: DesktopVaultLauncherHost;
|
|
10
|
+
onCreate(): Promise<void>;
|
|
11
|
+
onOpen(): Promise<void>;
|
|
12
|
+
onOpenRecent(profile: VaultProfile): Promise<void>;
|
|
13
|
+
onClose?(): void;
|
|
14
|
+
};
|
|
15
|
+
declare const DesktopVaultLauncher: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
16
|
+
type DesktopVaultLauncher = ReturnType<typeof DesktopVaultLauncher>;
|
|
17
|
+
export default DesktopVaultLauncher;
|