@dxos/plugin-native 0.8.4-main.fd6878d → 0.8.4-staging.60fe92afc8
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/LICENSE +102 -5
- package/PLUGIN.mdl +284 -0
- package/README.md +1 -1
- package/dist/lib/neutral/NativePlugin.mjs +46 -0
- package/dist/lib/neutral/NativePlugin.mjs.map +7 -0
- package/dist/lib/neutral/NativeSettings-CLYG5TJH.mjs +113 -0
- package/dist/lib/neutral/NativeSettings-CLYG5TJH.mjs.map +7 -0
- package/dist/lib/neutral/capabilities/index.mjs +17 -0
- package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
- package/dist/lib/neutral/chunk-75E24KAJ.mjs +34 -0
- package/dist/lib/neutral/chunk-75E24KAJ.mjs.map +7 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs +10 -0
- package/dist/lib/neutral/chunk-J5LGTIGS.mjs.map +7 -0
- package/dist/lib/neutral/components/index.mjs +9 -0
- package/dist/lib/neutral/components/index.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +8 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/meta.mjs +8 -0
- package/dist/lib/neutral/meta.mjs.map +7 -0
- package/dist/lib/{browser/ollama-MF7YNEDS.mjs → neutral/ollama-BHWFTROM.mjs} +20 -31
- package/dist/lib/neutral/ollama-BHWFTROM.mjs.map +7 -0
- package/dist/lib/neutral/plugin.mjs +12 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/neutral/react-surface-ETNGHN4X.mjs +27 -0
- package/dist/lib/neutral/react-surface-ETNGHN4X.mjs.map +7 -0
- package/dist/lib/neutral/settings-HF6TQNU4.mjs +28 -0
- package/dist/lib/neutral/settings-HF6TQNU4.mjs.map +7 -0
- package/dist/lib/neutral/spotlight-listener-K7S6LGL3.mjs +48 -0
- package/dist/lib/neutral/spotlight-listener-K7S6LGL3.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +35 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/lib/neutral/types/index.mjs +31 -0
- package/dist/lib/neutral/types/index.mjs.map +7 -0
- package/dist/lib/neutral/updater-JEBXWSSK.mjs +244 -0
- package/dist/lib/neutral/updater-JEBXWSSK.mjs.map +7 -0
- package/dist/types/src/NativePlugin.d.ts +3 -1
- package/dist/types/src/NativePlugin.d.ts.map +1 -1
- package/dist/types/src/capabilities/index.d.ts +6 -2
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/ollama.d.ts +5 -4
- package/dist/types/src/capabilities/ollama.d.ts.map +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +5 -0
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
- package/dist/types/src/capabilities/settings.d.ts +7 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/capabilities/spotlight-listener.d.ts +8 -0
- package/dist/types/src/capabilities/spotlight-listener.d.ts.map +1 -0
- package/dist/types/src/capabilities/updater.d.ts +4 -2
- package/dist/types/src/capabilities/updater.d.ts.map +1 -1
- package/dist/types/src/components/NativeSettings/NativeSettings.d.ts +6 -0
- package/dist/types/src/components/NativeSettings/NativeSettings.d.ts.map +1 -0
- package/dist/types/src/components/NativeSettings/index.d.ts +2 -0
- package/dist/types/src/components/NativeSettings/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +3 -0
- package/dist/types/src/components/index.d.ts.map +1 -0
- package/dist/types/src/constants.d.ts +2 -0
- package/dist/types/src/constants.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +2 -3
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/plugin.d.ts +3 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +20 -6
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types/NativeCapabilities.d.ts +5 -0
- package/dist/types/src/types/NativeCapabilities.d.ts.map +1 -0
- package/dist/types/src/types/Settings.d.ts +5 -0
- package/dist/types/src/types/Settings.d.ts.map +1 -0
- package/dist/types/src/types/Update.d.ts +30 -0
- package/dist/types/src/types/Update.d.ts.map +1 -0
- package/dist/types/src/types/index.d.ts +4 -0
- package/dist/types/src/types/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +79 -24
- package/src/NativePlugin.tsx +34 -22
- package/src/capabilities/index.ts +6 -3
- package/src/capabilities/ollama.ts +32 -23
- package/src/capabilities/react-surface.tsx +29 -0
- package/src/capabilities/settings.ts +31 -0
- package/src/capabilities/spotlight-listener.ts +58 -0
- package/src/capabilities/updater.ts +164 -49
- package/src/components/NativeSettings/NativeSettings.tsx +132 -0
- package/src/components/NativeSettings/index.ts +5 -0
- package/src/components/index.ts +7 -0
- package/src/constants.ts +7 -0
- package/src/index.ts +0 -1
- package/src/meta.ts +26 -6
- package/src/plugin.ts +9 -0
- package/src/translations.ts +20 -5
- package/src/types/NativeCapabilities.ts +16 -0
- package/src/types/Settings.ts +11 -0
- package/src/types/Update.ts +24 -0
- package/src/types/index.ts +7 -0
- package/src/vite-env.d.ts +10 -0
- package/dist/lib/browser/chunk-E2JDGVJI.mjs +0 -12
- package/dist/lib/browser/chunk-E2JDGVJI.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -52
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/ollama-MF7YNEDS.mjs.map +0 -7
- package/dist/lib/browser/updater-MCAQYHED.mjs +0 -133
- package/dist/lib/browser/updater-MCAQYHED.mjs.map +0 -7
package/package.json
CHANGED
|
@@ -1,50 +1,105 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-native",
|
|
3
|
-
"version": "0.8.4-
|
|
3
|
+
"version": "0.8.4-staging.60fe92afc8",
|
|
4
4
|
"description": "Composer plugin that runs when on a native device.",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
7
|
-
"
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/dxos/dxos"
|
|
10
|
+
},
|
|
11
|
+
"license": "FSL-1.1-Apache-2.0",
|
|
8
12
|
"author": "DXOS.org",
|
|
9
13
|
"sideEffects": true,
|
|
10
14
|
"type": "module",
|
|
15
|
+
"imports": {
|
|
16
|
+
"#capabilities": {
|
|
17
|
+
"source": "./src/capabilities/index.ts",
|
|
18
|
+
"types": "./dist/types/src/capabilities/index.d.ts",
|
|
19
|
+
"default": "./dist/lib/neutral/capabilities/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"#components": {
|
|
22
|
+
"source": "./src/components/index.ts",
|
|
23
|
+
"types": "./dist/types/src/components/index.d.ts",
|
|
24
|
+
"default": "./dist/lib/neutral/components/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"#meta": {
|
|
27
|
+
"source": "./src/meta.ts",
|
|
28
|
+
"types": "./dist/types/src/meta.d.ts",
|
|
29
|
+
"default": "./dist/lib/neutral/meta.mjs"
|
|
30
|
+
},
|
|
31
|
+
"#plugin": {
|
|
32
|
+
"source": "./src/NativePlugin.tsx",
|
|
33
|
+
"types": "./dist/types/src/NativePlugin.d.ts",
|
|
34
|
+
"default": "./dist/lib/neutral/NativePlugin.mjs"
|
|
35
|
+
},
|
|
36
|
+
"#translations": {
|
|
37
|
+
"source": "./src/translations.ts",
|
|
38
|
+
"types": "./dist/types/src/translations.d.ts",
|
|
39
|
+
"default": "./dist/lib/neutral/translations.mjs"
|
|
40
|
+
},
|
|
41
|
+
"#types": {
|
|
42
|
+
"source": "./src/types/index.ts",
|
|
43
|
+
"types": "./dist/types/src/types/index.d.ts",
|
|
44
|
+
"default": "./dist/lib/neutral/types/index.mjs"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
11
47
|
"exports": {
|
|
12
48
|
".": {
|
|
13
49
|
"source": "./src/index.ts",
|
|
14
50
|
"types": "./dist/types/src/index.d.ts",
|
|
15
|
-
"
|
|
51
|
+
"default": "./dist/lib/neutral/index.mjs"
|
|
52
|
+
},
|
|
53
|
+
"./assets/PLUGIN.mdl": "./PLUGIN.mdl",
|
|
54
|
+
"./plugin": {
|
|
55
|
+
"source": "./src/plugin.ts",
|
|
56
|
+
"types": "./dist/types/src/plugin.d.ts",
|
|
57
|
+
"default": "./dist/lib/neutral/plugin.mjs"
|
|
58
|
+
},
|
|
59
|
+
"./translations": {
|
|
60
|
+
"source": "./src/translations.ts",
|
|
61
|
+
"types": "./dist/types/src/translations.d.ts",
|
|
62
|
+
"default": "./dist/lib/neutral/translations.mjs"
|
|
16
63
|
}
|
|
17
64
|
},
|
|
18
65
|
"types": "dist/types/src/index.d.ts",
|
|
19
|
-
"typesVersions": {
|
|
20
|
-
"*": {}
|
|
21
|
-
},
|
|
22
66
|
"files": [
|
|
23
67
|
"dist",
|
|
24
|
-
"src"
|
|
68
|
+
"src",
|
|
69
|
+
"PLUGIN.mdl"
|
|
25
70
|
],
|
|
26
71
|
"dependencies": {
|
|
27
|
-
"@effect/
|
|
28
|
-
"@
|
|
29
|
-
"@tauri-apps/
|
|
30
|
-
"@tauri-apps/plugin-
|
|
31
|
-
"@tauri-apps/plugin-
|
|
32
|
-
"
|
|
33
|
-
"@
|
|
34
|
-
"
|
|
35
|
-
"@dxos/
|
|
36
|
-
"@dxos/
|
|
37
|
-
"@dxos/
|
|
38
|
-
"@dxos/
|
|
39
|
-
"@dxos/
|
|
72
|
+
"@effect-atom/atom-react": "^0.5.0",
|
|
73
|
+
"@effect/platform": "0.96.1",
|
|
74
|
+
"@tauri-apps/api": "^2.11.0",
|
|
75
|
+
"@tauri-apps/plugin-os": "^2.3.2",
|
|
76
|
+
"@tauri-apps/plugin-process": "^2.3.1",
|
|
77
|
+
"@tauri-apps/plugin-shell": "^2.3.5",
|
|
78
|
+
"@tauri-apps/plugin-updater": "^2.10.1",
|
|
79
|
+
"date-fns": "^3.6.0",
|
|
80
|
+
"@dxos/app-framework": "0.8.4-staging.60fe92afc8",
|
|
81
|
+
"@dxos/app-toolkit": "0.8.4-staging.60fe92afc8",
|
|
82
|
+
"@dxos/ai": "0.8.4-staging.60fe92afc8",
|
|
83
|
+
"@dxos/log": "0.8.4-staging.60fe92afc8",
|
|
84
|
+
"@dxos/effect": "0.8.4-staging.60fe92afc8",
|
|
85
|
+
"@dxos/plugin-assistant": "0.8.4-staging.60fe92afc8",
|
|
86
|
+
"@dxos/util": "0.8.4-staging.60fe92afc8",
|
|
87
|
+
"@dxos/keys": "0.8.4-staging.60fe92afc8"
|
|
40
88
|
},
|
|
41
89
|
"devDependencies": {
|
|
42
|
-
"
|
|
43
|
-
"
|
|
90
|
+
"@types/react": "~19.2.7",
|
|
91
|
+
"effect": "3.21.3",
|
|
92
|
+
"react": "~19.2.3",
|
|
93
|
+
"vite": "^8.0.16",
|
|
94
|
+
"@dxos/plugin-testing": "0.8.4-staging.60fe92afc8",
|
|
95
|
+
"@dxos/react-ui": "0.8.4-staging.60fe92afc8",
|
|
96
|
+
"@dxos/react-ui-form": "0.8.4-staging.60fe92afc8"
|
|
44
97
|
},
|
|
45
98
|
"peerDependencies": {
|
|
46
|
-
"effect": "
|
|
47
|
-
"
|
|
99
|
+
"effect": "3.21.3",
|
|
100
|
+
"react": "~19.2.3",
|
|
101
|
+
"@dxos/react-ui": "0.8.4-staging.60fe92afc8",
|
|
102
|
+
"@dxos/react-ui-form": "0.8.4-staging.60fe92afc8"
|
|
48
103
|
},
|
|
49
104
|
"publishConfig": {
|
|
50
105
|
"access": "public"
|
package/src/NativePlugin.tsx
CHANGED
|
@@ -2,28 +2,40 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { ActivationEvents, Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { AppPlugin } from '@dxos/app-toolkit';
|
|
6
7
|
import { AssistantEvents } from '@dxos/plugin-assistant';
|
|
7
8
|
|
|
8
|
-
import { Ollama, Updater } from '
|
|
9
|
-
import { meta } from '
|
|
10
|
-
import { translations } from '
|
|
9
|
+
import { NativeSettings, Ollama, ReactSurface, SpotlightListener, Updater } from '#capabilities';
|
|
10
|
+
import { meta } from '#meta';
|
|
11
|
+
import { translations } from '#translations';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
13
|
+
// eslint-disable-next-line import/no-relative-packages
|
|
14
|
+
import pluginSpec from '../PLUGIN.mdl?raw';
|
|
15
|
+
|
|
16
|
+
export const NativePlugin = Plugin.define(meta).pipe(
|
|
17
|
+
AppPlugin.addSettingsModule({ activate: NativeSettings }),
|
|
18
|
+
AppPlugin.addSurfaceModule({ activate: ReactSurface }),
|
|
19
|
+
AppPlugin.addTranslationsModule({ translations }),
|
|
20
|
+
Plugin.addModule({
|
|
21
|
+
id: 'spotlight-listener',
|
|
22
|
+
activatesOn: ActivationEvents.ProcessManagerReady,
|
|
23
|
+
activate: SpotlightListener,
|
|
24
|
+
}),
|
|
25
|
+
Plugin.addModule({
|
|
26
|
+
id: 'updater',
|
|
27
|
+
activatesOn: ActivationEvents.ProcessManagerReady,
|
|
28
|
+
activate: Updater,
|
|
29
|
+
}),
|
|
30
|
+
Plugin.addModule({
|
|
31
|
+
id: 'ollama',
|
|
32
|
+
activatesOn: AssistantEvents.SetupAiServiceProviders,
|
|
33
|
+
activate: Ollama,
|
|
34
|
+
}),
|
|
35
|
+
AppPlugin.addPluginAssetModule({
|
|
36
|
+
asset: { pluginId: meta.id, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },
|
|
37
|
+
}),
|
|
38
|
+
Plugin.make,
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export default NativePlugin;
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Capability } from '@dxos/app-framework';
|
|
6
6
|
|
|
7
|
-
export const
|
|
8
|
-
export const Ollama = lazy(() => import('./ollama'));
|
|
7
|
+
export const NativeSettings = Capability.lazy('NativeSettings', () => import('./settings'));
|
|
8
|
+
export const Ollama = Capability.lazy('Ollama', () => import('./ollama'));
|
|
9
|
+
export const ReactSurface = Capability.lazy('ReactSurface', () => import('./react-surface'));
|
|
10
|
+
export const SpotlightListener = Capability.lazy('SpotlightListener', () => import('./spotlight-listener'));
|
|
11
|
+
export const Updater = Capability.lazy('Updater', () => import('./updater'));
|
|
@@ -2,34 +2,41 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import * as FetchHttpClient from '@effect/platform/FetchHttpClient';
|
|
6
|
+
import * as HttpClient from '@effect/platform/HttpClient';
|
|
6
7
|
import { Command } from '@tauri-apps/plugin-shell';
|
|
7
|
-
import
|
|
8
|
+
import * as Context from 'effect/Context';
|
|
9
|
+
import * as Effect from 'effect/Effect';
|
|
10
|
+
import * as Layer from 'effect/Layer';
|
|
11
|
+
import * as ManagedRuntime from 'effect/ManagedRuntime';
|
|
8
12
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
13
|
+
import { type AiModelResolver } from '@dxos/ai';
|
|
14
|
+
import { OllamaResolver } from '@dxos/ai/resolvers';
|
|
15
|
+
import { Capability } from '@dxos/app-framework';
|
|
16
|
+
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
11
17
|
import { log } from '@dxos/log';
|
|
12
|
-
import { AssistantCapabilities } from '@dxos/plugin-assistant';
|
|
13
18
|
|
|
14
|
-
// Running ollama on non-standard port.
|
|
19
|
+
// NOTE: Running ollama on non-standard port (config Tauri).
|
|
15
20
|
const OLLAMA_HOST = 'http://localhost:21434';
|
|
16
21
|
|
|
17
|
-
export
|
|
18
|
-
const runtime = ManagedRuntime.make(OllamaSidecar.layerLive);
|
|
22
|
+
export type OllamaCapabilities = Capability.Capability<typeof AppCapabilities.AiModelResolver>;
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
runtime
|
|
23
|
-
);
|
|
24
|
+
export default Capability.makeModule<[], OllamaCapabilities>(() =>
|
|
25
|
+
Effect.sync(() => {
|
|
26
|
+
const runtime = ManagedRuntime.make(OllamaSidecar.layerLive);
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
// Layer for the sidecar but the lifecycle is managed by the runtime.
|
|
29
|
+
const sidecarLayer = Layer.effectContext(
|
|
30
|
+
runtime.runtimeEffect.pipe(Effect.map((rt) => rt.context.pipe(Context.pick(OllamaSidecar)))),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return Capability.contributes(
|
|
34
|
+
AppCapabilities.AiModelResolver,
|
|
35
|
+
OllamaSidecarModelResolver.pipe(Layer.provide(sidecarLayer)),
|
|
36
|
+
() => Effect.tryPromise(() => runtime.dispose()),
|
|
37
|
+
);
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
33
40
|
|
|
34
41
|
class OllamaSidecar extends Context.Tag('@dxos/plugin-native/OllamaSidecar')<
|
|
35
42
|
OllamaSidecar,
|
|
@@ -48,7 +55,9 @@ class OllamaSidecar extends Context.Tag('@dxos/plugin-native/OllamaSidecar')<
|
|
|
48
55
|
});
|
|
49
56
|
|
|
50
57
|
// NOTE: Keeping those as console.log for consistent formatting.
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
51
59
|
command.stdout.on('data', (data) => console.log('[ollama]', data.toString()));
|
|
60
|
+
// eslint-disable-next-line no-console
|
|
52
61
|
command.stderr.on('data', (data) => console.error('[ollama]', data.toString()));
|
|
53
62
|
command.on('close', (code) => log.info('Ollama process exited', { code }));
|
|
54
63
|
command.on('error', (error) => log.error('Ollama error', { error }));
|
|
@@ -67,12 +76,12 @@ class OllamaSidecar extends Context.Tag('@dxos/plugin-native/OllamaSidecar')<
|
|
|
67
76
|
);
|
|
68
77
|
}
|
|
69
78
|
|
|
70
|
-
const OllamaSidecarModelResolver: Layer.Layer<
|
|
79
|
+
const OllamaSidecarModelResolver: Layer.Layer<AiModelResolver.AiModelResolver, never, OllamaSidecar> =
|
|
71
80
|
Layer.unwrapEffect(
|
|
72
81
|
Effect.gen(function* () {
|
|
73
82
|
const { endpoint } = yield* OllamaSidecar;
|
|
74
|
-
return
|
|
75
|
-
|
|
83
|
+
return OllamaResolver.make({
|
|
84
|
+
endpoint,
|
|
76
85
|
transformClient: HttpClient.withTracerPropagation(false),
|
|
77
86
|
});
|
|
78
87
|
}),
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import React from 'react';
|
|
7
|
+
|
|
8
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
9
|
+
import { Surface, useSettingsState } from '@dxos/app-framework/ui';
|
|
10
|
+
import { AppSurface } from '@dxos/app-toolkit/ui';
|
|
11
|
+
|
|
12
|
+
import { NativeSettings } from '#components';
|
|
13
|
+
import { meta } from '#meta';
|
|
14
|
+
import { type Settings } from '#types';
|
|
15
|
+
|
|
16
|
+
export default Capability.makeModule(() =>
|
|
17
|
+
Effect.succeed(
|
|
18
|
+
Capability.contributes(Capabilities.ReactSurface, [
|
|
19
|
+
Surface.create({
|
|
20
|
+
id: 'pluginSettings',
|
|
21
|
+
filter: AppSurface.settings(AppSurface.Article, meta.id),
|
|
22
|
+
component: ({ data: { subject } }) => {
|
|
23
|
+
const { settings, updateSettings } = useSettingsState<Settings.Settings>(subject.atom);
|
|
24
|
+
return <NativeSettings settings={settings} onSettingsChange={updateSettings} />;
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
]),
|
|
28
|
+
),
|
|
29
|
+
);
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capability } from '@dxos/app-framework';
|
|
8
|
+
import { AppCapabilities } from '@dxos/app-toolkit';
|
|
9
|
+
import { createKvsStore } from '@dxos/effect';
|
|
10
|
+
|
|
11
|
+
import { meta } from '#meta';
|
|
12
|
+
import { NativeCapabilities, Settings } from '#types';
|
|
13
|
+
|
|
14
|
+
export default Capability.makeModule(() =>
|
|
15
|
+
Effect.sync(() => {
|
|
16
|
+
const settingsAtom = createKvsStore({
|
|
17
|
+
key: meta.id,
|
|
18
|
+
schema: Settings.Settings,
|
|
19
|
+
defaultValue: () => ({}),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
return [
|
|
23
|
+
Capability.contributes(NativeCapabilities.Settings, settingsAtom),
|
|
24
|
+
Capability.contributes(AppCapabilities.Settings, {
|
|
25
|
+
prefix: meta.id,
|
|
26
|
+
schema: Settings.Settings,
|
|
27
|
+
atom: settingsAtom,
|
|
28
|
+
}),
|
|
29
|
+
];
|
|
30
|
+
}),
|
|
31
|
+
);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
8
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
9
|
+
import { log } from '@dxos/log';
|
|
10
|
+
|
|
11
|
+
// TODO(wittjosiah): Formalize with a stricter schema if we evolve this protocol.
|
|
12
|
+
type SpotlightInvokePayload = {
|
|
13
|
+
operation: string;
|
|
14
|
+
payload?: Record<string, any>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Listens for spotlight:invoke events from the popover window and dispatches the corresponding operation.
|
|
19
|
+
*/
|
|
20
|
+
export default Capability.makeModule(
|
|
21
|
+
Effect.fnUntraced(function* () {
|
|
22
|
+
const { invokePromise } = yield* Capability.get(Capabilities.OperationInvoker);
|
|
23
|
+
|
|
24
|
+
const unlisten = yield* Effect.promise(async () => {
|
|
25
|
+
const { listen } = await import('@tauri-apps/api/event');
|
|
26
|
+
const { getCurrentWindow } = await import('@tauri-apps/api/window');
|
|
27
|
+
|
|
28
|
+
return listen<SpotlightInvokePayload>('spotlight:invoke', async (event) => {
|
|
29
|
+
const { operation, payload } = event.payload;
|
|
30
|
+
log.info('Received spotlight invoke event', { operation, payload });
|
|
31
|
+
try {
|
|
32
|
+
switch (operation) {
|
|
33
|
+
case 'open':
|
|
34
|
+
await invokePromise(LayoutOperation.Open, payload as any);
|
|
35
|
+
break;
|
|
36
|
+
case 'switch-workspace':
|
|
37
|
+
await invokePromise(LayoutOperation.SwitchWorkspace, payload as any);
|
|
38
|
+
break;
|
|
39
|
+
default:
|
|
40
|
+
log.warn('Unknown spotlight operation', { operation });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const mainWindow = getCurrentWindow();
|
|
44
|
+
await mainWindow.show();
|
|
45
|
+
await mainWindow.setFocus();
|
|
46
|
+
} catch (err) {
|
|
47
|
+
log.catch(err);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return Capability.contributes(Capabilities.Null, null, () =>
|
|
53
|
+
Effect.sync(() => {
|
|
54
|
+
unlisten();
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
}),
|
|
58
|
+
);
|
|
@@ -2,78 +2,193 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Atom } from '@effect-atom/atom-react';
|
|
5
6
|
import { type } from '@tauri-apps/plugin-os';
|
|
6
7
|
import { relaunch } from '@tauri-apps/plugin-process';
|
|
7
|
-
import
|
|
8
|
-
import
|
|
8
|
+
import * as Updater from '@tauri-apps/plugin-updater';
|
|
9
|
+
import * as Duration from 'effect/Duration';
|
|
10
|
+
import * as Effect from 'effect/Effect';
|
|
11
|
+
import * as Fiber from 'effect/Fiber';
|
|
12
|
+
import * as Match from 'effect/Match';
|
|
13
|
+
import * as Schedule from 'effect/Schedule';
|
|
9
14
|
|
|
10
|
-
import { Capabilities,
|
|
15
|
+
import { Capabilities, Capability } from '@dxos/app-framework';
|
|
16
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
11
17
|
import { log } from '@dxos/log';
|
|
12
18
|
|
|
13
|
-
import {
|
|
19
|
+
import { meta } from '#meta';
|
|
20
|
+
import { NativeCapabilities, type Update } from '#types';
|
|
21
|
+
|
|
22
|
+
import { TAURI_LOCALHOST_PORT } from '../constants';
|
|
14
23
|
|
|
15
24
|
const SUPPORTS_OTA = ['linux', 'macos', 'windows'];
|
|
16
25
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return
|
|
26
|
+
/** Safe wrapper around Updater.check(). Distinguishes "no update" (ok with null update) from a thrown error. */
|
|
27
|
+
const safeCheck = async (): Promise<{ ok: true; update: Updater.Update | null } | { ok: false; error: string }> => {
|
|
28
|
+
try {
|
|
29
|
+
return { ok: true, update: await Updater.check() };
|
|
30
|
+
} catch (error) {
|
|
31
|
+
log.error('failed to check for updates', { error });
|
|
32
|
+
return { ok: false, error: formatError(error) };
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/** Safe wrapper around update.downloadAndInstall(). */
|
|
37
|
+
const safeDownloadAndInstall = async (
|
|
38
|
+
update: Updater.Update,
|
|
39
|
+
onEvent: (event: Updater.DownloadEvent) => void,
|
|
40
|
+
): Promise<{ ok: true } | { ok: false; error: string }> => {
|
|
41
|
+
try {
|
|
42
|
+
await update.downloadAndInstall(onEvent);
|
|
43
|
+
return { ok: true };
|
|
44
|
+
} catch (error) {
|
|
45
|
+
log.error('failed to download and install update', { error });
|
|
46
|
+
return { ok: false, error: formatError(error) };
|
|
21
47
|
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** Extract a user-readable error string from whatever the Tauri updater threw. */
|
|
51
|
+
const formatError = (error: unknown): string => {
|
|
52
|
+
if (typeof error === 'string') {
|
|
53
|
+
return error;
|
|
54
|
+
}
|
|
55
|
+
if (error instanceof Error) {
|
|
56
|
+
return error.message;
|
|
57
|
+
}
|
|
58
|
+
return String(error);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export default Capability.makeModule(
|
|
62
|
+
Effect.fnUntraced(function* () {
|
|
63
|
+
const platform = type();
|
|
64
|
+
const isDevServer = window.location.port !== TAURI_LOCALHOST_PORT;
|
|
65
|
+
const enabled = SUPPORTS_OTA.includes(platform) && !isDevServer;
|
|
66
|
+
|
|
67
|
+
const registry = yield* Capability.get(Capabilities.AtomRegistry);
|
|
68
|
+
const { invoke } = yield* Capability.get(Capabilities.OperationInvoker);
|
|
22
69
|
|
|
23
|
-
|
|
70
|
+
const statusAtom = Atom.make<Update.Status>(enabled ? { kind: 'idle' } : { kind: 'unsupported' }).pipe(
|
|
71
|
+
Atom.keepAlive,
|
|
72
|
+
);
|
|
24
73
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
74
|
+
// Updater.Update is a class with instance methods (downloadAndInstall) and can't live in an
|
|
75
|
+
// atom value; cache it here between check and install.
|
|
76
|
+
let pendingUpdate: Updater.Update | null = null;
|
|
77
|
+
|
|
78
|
+
// Core: check for an update and update the status atom. Returns the update if available.
|
|
79
|
+
const doCheck = async (): Promise<Updater.Update | null> => {
|
|
80
|
+
registry.set(statusAtom, { kind: 'checking' });
|
|
81
|
+
log.info('checking for updates');
|
|
82
|
+
const result = await safeCheck();
|
|
83
|
+
if (!result.ok) {
|
|
84
|
+
pendingUpdate = null;
|
|
85
|
+
registry.set(statusAtom, { kind: 'failed', error: result.error });
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
const update = result.update;
|
|
89
|
+
if (!update?.available) {
|
|
90
|
+
pendingUpdate = null;
|
|
91
|
+
registry.set(statusAtom, { kind: 'up-to-date', checkedAt: Date.now() });
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
log.info('update available', { version: update.version });
|
|
95
|
+
pendingUpdate = update;
|
|
96
|
+
registry.set(statusAtom, { kind: 'available', version: update.version });
|
|
97
|
+
return update;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// Core: download + install the cached pending update, streaming progress to the status atom.
|
|
101
|
+
const doInstall = async (): Promise<boolean> => {
|
|
102
|
+
const update = pendingUpdate;
|
|
103
|
+
if (!update) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
32
106
|
let downloaded = 0;
|
|
33
107
|
let contentLength = 0;
|
|
34
|
-
|
|
35
|
-
const
|
|
108
|
+
registry.set(statusAtom, { kind: 'downloading', downloaded: 0, contentLength: 0 });
|
|
109
|
+
const onEvent = Match.type<Updater.DownloadEvent>().pipe(
|
|
36
110
|
Match.when({ event: 'Started' }, (event) => {
|
|
37
111
|
contentLength = event.data.contentLength ?? 0;
|
|
38
|
-
|
|
112
|
+
registry.set(statusAtom, { kind: 'downloading', downloaded, contentLength });
|
|
39
113
|
}),
|
|
40
114
|
Match.when({ event: 'Progress' }, (event) => {
|
|
41
115
|
downloaded += event.data.chunkLength;
|
|
42
|
-
|
|
116
|
+
registry.set(statusAtom, { kind: 'downloading', downloaded, contentLength });
|
|
43
117
|
}),
|
|
44
118
|
Match.when({ event: 'Finished' }, () => {
|
|
45
119
|
log.info('download completed');
|
|
46
120
|
}),
|
|
47
121
|
Match.exhaustive,
|
|
48
122
|
);
|
|
123
|
+
const result = await safeDownloadAndInstall(update, onEvent);
|
|
124
|
+
if (result.ok) {
|
|
125
|
+
registry.set(statusAtom, { kind: 'ready' });
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
registry.set(statusAtom, { kind: 'failed', error: result.error });
|
|
129
|
+
return false;
|
|
130
|
+
};
|
|
49
131
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
132
|
+
const manager: Update.Manager = {
|
|
133
|
+
status: statusAtom,
|
|
134
|
+
check: async () => {
|
|
135
|
+
if (!enabled) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
await doCheck();
|
|
139
|
+
},
|
|
140
|
+
install: async () => {
|
|
141
|
+
if (!enabled) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
await doInstall();
|
|
145
|
+
},
|
|
146
|
+
relaunch: async () => {
|
|
147
|
+
await relaunch();
|
|
148
|
+
},
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const managerContribution = Capability.contributes(NativeCapabilities.UpdateManager, manager);
|
|
152
|
+
|
|
153
|
+
if (!enabled) {
|
|
154
|
+
log.info('updater disabled', { platform, port: window.location.port });
|
|
155
|
+
return [managerContribution];
|
|
66
156
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
157
|
+
|
|
158
|
+
// Background flow: periodic check + auto-install + toast when ready.
|
|
159
|
+
// The toast is the entry point for users who weren't watching the settings panel.
|
|
160
|
+
const backgroundAction = Effect.gen(function* () {
|
|
161
|
+
const update = yield* Effect.promise(() => doCheck());
|
|
162
|
+
if (!update) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
const ok = yield* Effect.promise(() => doInstall());
|
|
166
|
+
if (!ok) {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
yield* invoke(LayoutOperation.AddToast, {
|
|
170
|
+
id: `${meta.id}.update-ready`,
|
|
171
|
+
title: ['update-ready.label', { ns: meta.id }],
|
|
172
|
+
description: ['update-ready.description', { ns: meta.id }],
|
|
173
|
+
duration: Infinity,
|
|
174
|
+
actionLabel: ['update.label', { ns: meta.id }],
|
|
175
|
+
actionAlt: ['update.alt', { ns: meta.id }],
|
|
176
|
+
onAction: () => relaunch(),
|
|
177
|
+
});
|
|
178
|
+
return false;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
const schedule = Schedule.fixed(Duration.hours(1)).pipe(
|
|
182
|
+
Schedule.whileInput((keepChecking: boolean) => keepChecking),
|
|
183
|
+
);
|
|
184
|
+
const fiber = yield* backgroundAction.pipe(Effect.repeat(schedule), Effect.forkDaemon);
|
|
185
|
+
log.info('updater module initialized, update check scheduled');
|
|
186
|
+
|
|
187
|
+
return [
|
|
188
|
+
managerContribution,
|
|
189
|
+
// Return the interruption effect directly; Fiber.interrupt is async and would throw
|
|
190
|
+
// AsyncFiberException if wrapped in Effect.runSync.
|
|
191
|
+
Capability.contributes(Capabilities.Null, null, () => Fiber.interrupt(fiber)),
|
|
192
|
+
];
|
|
193
|
+
}),
|
|
194
|
+
);
|