@@ -12,6 +15,7 @@
:title="setting.title"
:description="setting.description"
:settings="setting.settings"
+ :is-first="i == 0"
:namespace
/>
@@ -38,6 +42,7 @@ defineProps<{
title?: string;
description?: string;
namespace: string[];
+ isFirst?: boolean;
settings: SettingsPropertyDefinition[];
}>();
diff --git a/packages/safelight/src/views/Editor/Editor.vue b/packages/safelight/src/views/Editor/Editor.vue
index 54cd14d8..5abe7800 100644
--- a/packages/safelight/src/views/Editor/Editor.vue
+++ b/packages/safelight/src/views/Editor/Editor.vue
@@ -43,13 +43,14 @@ import { CurrentProject } from '@/stores/currentProject';
import { useEditor } from '@/stores/useEditor';
import { useProject } from '@/stores/useProject';
import { PhFile, PhGear, PhSignOut } from '@phosphor-icons/vue';
+import { SettingsManager } from '@safelight/shared/Settings/SettingsManager';
import ConfirmDialog from 'primevue/confirmdialog';
import Menubar from 'primevue/menubar';
import type { MenuItem } from 'primevue/menuitem';
import Toolbar from 'primevue/toolbar';
import { useConfirm } from 'primevue/useconfirm';
import { useDialog } from 'primevue/usedialog';
-import { defineAsyncComponent, onBeforeUnmount, onMounted, watch } from 'vue';
+import { onBeforeUnmount, onMounted, watch } from 'vue';
const project = useProject();
const editor = useEditor();
@@ -68,26 +69,8 @@ const menuItems: MenuItem[] = [
label: 'Settings',
icon: PhGear as any,
disabled: false,
- command: async () => {
- const settingsComponent = defineAsyncComponent(
- () => import('@/components/Menu/Settings/Settings.vue')
- );
- dialog.open(settingsComponent, {
- props: {
- header: 'Settings',
- style: {
- width: '75vw',
- height: '80vh'
- },
- pt: { content: { style: { height: '100%' } } },
- breakpoints: {
- '960px': '80vw',
- '640px': '90vw'
- },
- modal: true,
- draggable: false
- }
- });
+ command: () => {
+ SettingsManager.openSettings(dialog);
}
},
{
@@ -107,7 +90,6 @@ onMounted(async () => {
await router.isReady();
if (!CurrentProject.isLoaded.value) {
const lastProject = CurrentProject.getSessionProject();
- console.log(lastProject);
if (lastProject) {
if (lastProject) {
CurrentProject.openProject(lastProject, false /* Already here */);
diff --git a/packages/shared/package.json b/packages/shared/package.json
index c01b4bbf..3dd27bf7 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -11,20 +11,14 @@
"test:run": "vitest run --ui false"
},
"dependencies": {
- "@phosphor-icons/vue": "^2.2.1",
- "@vueuse/core": "^10.9.0",
- "@vueuse/rxjs": "^10.10.0",
- "@vueuse/shared": "^10.10.0",
"dexie": "^4.0.7",
"dot-path-value": "^0.0.10",
"hash-wasm": "^4.11.0",
"luxon": "^3.4.4",
"mediainfo.js": "^0.3.1",
"mime-matcher": "^1.0.5",
- "primevue": "^3.52.0",
"rxjs": "^7.8.1",
- "uuid": "^9.0.1",
- "vue": "^3.4.29"
+ "uuid": "^9.0.1"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.4",
@@ -33,5 +27,14 @@
"@vue/tsconfig": "^0.5.1",
"fake-indexeddb": "^5.0.2",
"happy-dom": "^14.7.1"
+ },
+ "peerDependencies": {
+ "@phosphor-icons/vue": "~2",
+ "@vueuse/core": "~10",
+ "@vueuse/rxjs": "~10",
+ "@vueuse/shared": "~10",
+ "primevue": "~3",
+ "vue": "~3",
+ "@safelight/safelight": "workspace:^"
}
}
diff --git a/packages/shared/src/Settings/SettingsManager.ts b/packages/shared/src/Settings/SettingsManager.ts
index e593bedc..2f76ce88 100644
--- a/packages/shared/src/Settings/SettingsManager.ts
+++ b/packages/shared/src/Settings/SettingsManager.ts
@@ -9,6 +9,7 @@ import {
type ComputedRef,
type Raw
} from 'vue';
+import { useDialog } from 'primevue/usedialog';
export class SettingsManager {
private static defaultNamespaces: SettingsNamespaceDefinition[] = [
@@ -20,15 +21,22 @@ export class SettingsManager {
{
name: 'editor',
title: 'Editor',
+ icon: defineAsyncComponent(async () => (await import('@phosphor-icons/vue')).PhSidebar),
childNamespaces: [
{
name: 'playback',
title: 'Playback',
+ icon: defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhPlayPause
+ ),
settings: []
},
{
name: 'timeline',
title: 'Timeline',
+ icon: defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhFilmStrip
+ ),
settings: [
{
type: 'boolean',
@@ -42,13 +50,30 @@ export class SettingsManager {
{
name: 'library',
title: 'Library',
- settings: []
+ icon: defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhFolders
+ ),
+ settings: [
+ {
+ type: 'group',
+ title: 'Media',
+ settings: []
+ },
+ {
+ type: 'group',
+ title: 'Files',
+ settings: []
+ }
+ ]
}
]
},
{
name: 'keyboard',
title: 'Keyboard',
+ icon: defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhKeyboard
+ ),
childNamespaces: [
{
name: 'hotkeys',
@@ -135,6 +160,31 @@ export class SettingsManager {
this.saveSettingsDebounced();
}
+ public static async openSettings(dialog: ReturnType, namespace?: string) {
+ const settingsComponent = defineAsyncComponent(
+ () => import('@safelight/safelight/src/components/Menu/Settings/Settings.vue')
+ );
+ dialog.open(settingsComponent, {
+ data: {
+ namespace
+ },
+ props: {
+ header: 'Settings',
+ style: {
+ width: '75vw',
+ height: '80vh'
+ },
+ pt: { content: { style: { height: '100%' } } },
+ breakpoints: {
+ '960px': '80vw',
+ '640px': '90vw'
+ },
+ modal: true,
+ draggable: false
+ }
+ });
+ }
+
private static saveSettingsDebounced() {
clearInterval(this.saveTimeout);
this.saveTimeout = setTimeout(() => this.saveSettings(), 250);
@@ -201,6 +251,7 @@ export class SettingsNamespace {
public name: string;
public childNamespaces!: SettingsNamespace[];
public settings: SettingsPropertyDefinition[];
+ public icon?: Raw;
constructor(
public pathArray: string[],
@@ -211,6 +262,7 @@ export class SettingsNamespace {
this.name = definition.name;
this.description = definition.description;
this.settings = definition.settings ?? [];
+ this.icon = definition.icon;
}
/**
@@ -235,6 +287,7 @@ export interface SettingsNamespaceDefinition {
description?: string;
settings?: SettingsPropertyDefinition[];
childNamespaces?: SettingsNamespaceDefinition[];
+ icon?: Raw;
}
export type SettingsPropertyDefinition =
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index ceb32c48..4292e739 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -287,17 +287,20 @@ importers:
packages/shared:
dependencies:
'@phosphor-icons/vue':
- specifier: ^2.2.1
+ specifier: ~2
version: 2.2.1(vue@3.4.29)
+ '@safelight/safelight':
+ specifier: workspace:^
+ version: link:../safelight
'@vueuse/core':
- specifier: ^10.9.0
- version: 10.10.0(vue@3.4.29)
+ specifier: ~10
+ version: 10.11.0(vue@3.4.29)
'@vueuse/rxjs':
- specifier: ^10.10.0
- version: 10.10.0(rxjs@7.8.1)(vue@3.4.29)
+ specifier: ~10
+ version: 10.11.0(rxjs@7.8.1)(vue@3.4.29)
'@vueuse/shared':
- specifier: ^10.10.0
- version: 10.10.0(vue@3.4.29)
+ specifier: ~10
+ version: 10.11.0(vue@3.4.29)
dexie:
specifier: ^4.0.7
version: 4.0.7
@@ -317,7 +320,7 @@ importers:
specifier: ^1.0.5
version: 1.0.5
primevue:
- specifier: ^3.52.0
+ specifier: ~3
version: 3.52.0(vue@3.4.29)
rxjs:
specifier: ^7.8.1
@@ -326,7 +329,7 @@ importers:
specifier: ^9.0.1
version: 9.0.1
vue:
- specifier: ^3.4.29
+ specifier: ~3
version: 3.4.29(typescript@5.4.5)
devDependencies:
'@tsconfig/node20':
@@ -2190,7 +2193,6 @@ packages:
- '@vue/composition-api'
- vue
dev: false
- optional: true
/@vueuse/gesture@2.0.0(vue@3.4.29):
resolution: {integrity: sha512-+F0bhhd8j+gxHaXG4fJgfokrkFfWenQ10MtrWOJk68B5UaTwtJm4EpsZFiVdluA3jpKExG6H+HtroJpvO7Qx0A==}
@@ -2275,7 +2277,6 @@ packages:
resolution: {integrity: sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==}
requiresBuild: true
dev: false
- optional: true
/@vueuse/rxjs@10.10.0(rxjs@7.8.1)(vue@3.4.29):
resolution: {integrity: sha512-gGziL1ImRb6M6P7BIPIWg9bk0qbZWIdSmXNwUlwlPi/tu6c4bBa4tfz4jqaL3ayQ2jOj8GTYea2Kg/8Afp+/ew==}
@@ -2303,7 +2304,6 @@ packages:
- '@vue/composition-api'
- vue
dev: false
- optional: true
/@vueuse/shared@10.10.0(vue@3.4.29):
resolution: {integrity: sha512-2aW33Ac0Uk0U+9yo3Ypg9s5KcR42cuehRWl7vnUHadQyFvCktseyxxEPBi1Eiq4D2yBGACOnqLZpx1eMc7g5Og==}
@@ -2323,7 +2323,6 @@ packages:
- '@vue/composition-api'
- vue
dev: false
- optional: true
/abbrev@2.0.0:
resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 9a36b405..6de82aee 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,2 +1,2 @@
packages:
- - 'packages/*'
+ - "packages/*"
From 6c292b46adcd2caa331b41042155ec72a29641e0 Mon Sep 17 00:00:00 2001
From: Joery <44531907+Joery-M@users.noreply.github.com>
Date: Sun, 30 Jun 2024 21:28:51 +0200
Subject: [PATCH 11/18] feat(Settings): :sparkles: Added resetting to default,
number setting, string setting
---
.../components/Editor/Timeline/Timeline.vue | 2 +
.../src/components/Menu/Settings/Settings.vue | 39 +++++---
.../SettingsProperties/BooleanSetting.vue | 32 +++++-
.../SettingsProperties/EnumSetting.vue | 8 +-
.../SettingsProperties/NumberSetting.vue | 85 +++++++++++++++-
.../SettingsProperties/SettingsGroup.vue | 2 +-
.../SettingsProperties/StringSetting.vue | 99 ++++++++++++++++++-
.../shared/src/Settings/SettingsManager.ts | 69 ++++++++++---
packages/timeline/src/Timeline.vue | 4 +-
9 files changed, 296 insertions(+), 44 deletions(-)
diff --git a/packages/safelight/src/components/Editor/Timeline/Timeline.vue b/packages/safelight/src/components/Editor/Timeline/Timeline.vue
index 8e6f43bc..2287f4b5 100644
--- a/packages/safelight/src/components/Editor/Timeline/Timeline.vue
+++ b/packages/safelight/src/components/Editor/Timeline/Timeline.vue
@@ -4,6 +4,7 @@
:playback-position="pbPos"
:fps="timeline?.framerate.value"
:invert-scroll-axes
+ :zoom-factor
class="h-full"
@update:playback-position="setPbPos"
/>
@@ -26,6 +27,7 @@ const pbPos = computed(() =>
: 0
);
const invertScrollAxes = SettingsManager.getSetting('editor.timeline.useTrackpad');
+const zoomFactor = SettingsManager.getSetting('editor.timeline.zoomFactor');
function setPbPos(pb?: number) {
if (pb !== undefined && timeline?.value) {
diff --git a/packages/safelight/src/components/Menu/Settings/Settings.vue b/packages/safelight/src/components/Menu/Settings/Settings.vue
index 8d9bbe62..6d7c19fa 100644
--- a/packages/safelight/src/components/Menu/Settings/Settings.vue
+++ b/packages/safelight/src/components/Menu/Settings/Settings.vue
@@ -9,11 +9,11 @@
selection-mode="single"
@node-select="onNodeSelect"
>
-
-
-
+
+
+
+
-
>('dialogRef');
onMounted(() => {
if (dialogRef?.value?.data && dialogRef.value.data.namespace) {
- selectedPath.value = dialogRef.value.data.namespace
- const namespaces = selectedPath.value.split('.')
- expandedKeys.value = {}
+ selectedPath.value = dialogRef.value.data.namespace;
+ const namespaces = selectedPath.value.split('.');
+ expandedKeys.value = {};
namespaces.forEach((ns) => {
- expandedKeys.value = {...expandedKeys.value, [ns]: true}
- })
+ expandedKeys.value = { ...expandedKeys.value, [ns]: true };
+ });
}
-})
+});
const tree = computed(() => {
return (Array.from(SettingsManager.settingsDefinition.values()) as SettingsNamespace[]).map(
@@ -84,16 +84,27 @@ function mapSettingsNs(ns: SettingsNamespace): TreeNode {
diff --git a/packages/safelight/src/components/Menu/Settings/SettingsProperties/BooleanSetting.vue b/packages/safelight/src/components/Menu/Settings/SettingsProperties/BooleanSetting.vue
index 41e43c02..c0f0b48a 100644
--- a/packages/safelight/src/components/Menu/Settings/SettingsProperties/BooleanSetting.vue
+++ b/packages/safelight/src/components/Menu/Settings/SettingsProperties/BooleanSetting.vue
@@ -3,6 +3,7 @@
+
+ (Default: {{ defaultValue }})
+
+
+
+
diff --git a/packages/safelight/src/components/Menu/Settings/SettingsProperties/EnumSetting.vue b/packages/safelight/src/components/Menu/Settings/SettingsProperties/EnumSetting.vue
index 960770d4..45e253a3 100644
--- a/packages/safelight/src/components/Menu/Settings/SettingsProperties/EnumSetting.vue
+++ b/packages/safelight/src/components/Menu/Settings/SettingsProperties/EnumSetting.vue
@@ -1,6 +1,10 @@
- {{ setting.title }}
-
+ {{ setting.title }}
+
+
+
diff --git a/packages/safelight/src/components/Menu/Settings/SettingsProperties/SettingsGroup.vue b/packages/safelight/src/components/Menu/Settings/SettingsProperties/SettingsGroup.vue
index 6d156520..b26921e5 100644
--- a/packages/safelight/src/components/Menu/Settings/SettingsProperties/SettingsGroup.vue
+++ b/packages/safelight/src/components/Menu/Settings/SettingsProperties/SettingsGroup.vue
@@ -5,7 +5,7 @@
-
+
diff --git a/packages/safelight/src/components/Menu/Settings/SettingsProperties/StringSetting.vue b/packages/safelight/src/components/Menu/Settings/SettingsProperties/StringSetting.vue
index 6194b27f..48fd5499 100644
--- a/packages/safelight/src/components/Menu/Settings/SettingsProperties/StringSetting.vue
+++ b/packages/safelight/src/components/Menu/Settings/SettingsProperties/StringSetting.vue
@@ -1,11 +1,102 @@
- {{ setting.title }}
-
+ {{ setting.title }}
+
+
+ (Default: {{ defaultValue }})
+
+
+
+
+
diff --git a/packages/shared/src/Settings/SettingsManager.ts b/packages/shared/src/Settings/SettingsManager.ts
index 2f76ce88..45579b72 100644
--- a/packages/shared/src/Settings/SettingsManager.ts
+++ b/packages/shared/src/Settings/SettingsManager.ts
@@ -1,4 +1,5 @@
import { getByPath, setByPath } from 'dot-path-value';
+import { useDialog } from 'primevue/usedialog';
import {
computed,
defineAsyncComponent,
@@ -9,7 +10,6 @@ import {
type ComputedRef,
type Raw
} from 'vue';
-import { useDialog } from 'primevue/usedialog';
export class SettingsManager {
private static defaultNamespaces: SettingsNamespaceDefinition[] = [
@@ -21,21 +21,27 @@ export class SettingsManager {
{
name: 'editor',
title: 'Editor',
- icon: defineAsyncComponent(async () => (await import('@phosphor-icons/vue')).PhSidebar),
+ icon: markRaw(
+ defineAsyncComponent(async () => (await import('@phosphor-icons/vue')).PhSidebar)
+ ),
childNamespaces: [
{
name: 'playback',
title: 'Playback',
- icon: defineAsyncComponent(
- async () => (await import('@phosphor-icons/vue')).PhPlayPause
+ icon: markRaw(
+ defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhPlayPause
+ )
),
settings: []
},
{
name: 'timeline',
title: 'Timeline',
- icon: defineAsyncComponent(
- async () => (await import('@phosphor-icons/vue')).PhFilmStrip
+ icon: markRaw(
+ defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhFilmStrip
+ )
),
settings: [
{
@@ -44,20 +50,43 @@ export class SettingsManager {
title: 'Trackpad mode',
description: 'Will inverse the axes on which the timeline will scroll.',
default: false
+ },
+ {
+ type: 'number',
+ name: 'zoomFactor',
+ title: 'Zoom factor',
+ description: 'The amount to zoom in and out by when scrolling.',
+ default: 2,
+ decimals: false,
+ range: true,
+ min: 1,
+ max: 100
}
]
},
{
name: 'library',
title: 'Library',
- icon: defineAsyncComponent(
- async () => (await import('@phosphor-icons/vue')).PhFolders
+ icon: markRaw(
+ defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhFolders
+ )
),
settings: [
{
type: 'group',
title: 'Media',
- settings: []
+ settings: [
+ {
+ type: 'string',
+ title: 'Test',
+ description: 'Test',
+ name: 'test',
+ pattern: /icle+/,
+ maxLength: 10,
+ default: 'icle'
+ }
+ ]
},
{
type: 'group',
@@ -71,8 +100,8 @@ export class SettingsManager {
{
name: 'keyboard',
title: 'Keyboard',
- icon: defineAsyncComponent(
- async () => (await import('@phosphor-icons/vue')).PhKeyboard
+ icon: markRaw(
+ defineAsyncComponent(async () => (await import('@phosphor-icons/vue')).PhKeyboard)
),
childNamespaces: [
{
@@ -268,14 +297,16 @@ export class SettingsNamespace {
/**
* Set the default values of each setting in the namespace.
*/
- public setDefaultValues() {
- this.settings.forEach((setting) => {
+ public setDefaultValues(settings = this.settings) {
+ settings.forEach((setting) => {
if (setting.type !== 'group' && setting.default !== undefined) {
setByPath(
SettingsManager.defaultSettings,
[...this.pathArray, setting.name].join('.'),
setting.default
);
+ } else if (setting.type == 'group') {
+ this.setDefaultValues(setting.settings);
}
});
}
@@ -327,6 +358,18 @@ export interface SettingsNumberProperty extends DefaultSettingsProperty {
default?: number;
min?: number;
max?: number;
+ /**
+ * Use a slider along side a number input.
+ *
+ * @requires {@link SettingsNumberProperty.min|min}
+ * @requires {@link SettingsNumberProperty.max|max}
+ */
+ range?: boolean;
+ /**
+ * Max number of decimal digits
+ *
+ * @default Infinity
+ */
decimals?: number | false;
}
diff --git a/packages/timeline/src/Timeline.vue b/packages/timeline/src/Timeline.vue
index 5621d436..def58154 100644
--- a/packages/timeline/src/Timeline.vue
+++ b/packages/timeline/src/Timeline.vue
@@ -36,7 +36,7 @@ import { useElementBounding, useEventListener, watchImmediate } from '@vueuse/co
import { useWheel } from '@vueuse/gesture';
import Splitter from 'primevue/splitter';
import SplitterPanel from 'primevue/splitterpanel';
-import { computed, provide, ref } from 'vue';
+import { computed, provide, ref, watchEffect } from 'vue';
import { TimelineViewport, type TimelineItem, type TimelineProps } from './index';
import LayerControl from './LayerControl.vue';
import PlaybackHead from './PlaybackHead.vue';
@@ -82,7 +82,7 @@ viewport.pbPos = playbackPosition;
provide('viewport', viewport);
-watchImmediate(props, () => {
+watchEffect(() => {
viewport.alignment.value = props.alignment;
viewport.zoomFactor.value = props.zoomFactor;
viewport.fps.value = props.fps ?? Infinity;
From e243c4c7d8c9aa73f866e4d8d165fbe5ef07effb Mon Sep 17 00:00:00 2001
From: Joery <44531907+Joery-M@users.noreply.github.com>
Date: Sun, 30 Jun 2024 21:59:44 +0200
Subject: [PATCH 12/18] feat(Settings): :sparkles: Added download button
---
.../src/components/Menu/Settings/Settings.vue | 18 +++++++++++++++++-
.../shared/src/Settings/SettingsManager.ts | 14 ++++++++++++++
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/packages/safelight/src/components/Menu/Settings/Settings.vue b/packages/safelight/src/components/Menu/Settings/Settings.vue
index 6d7c19fa..ae8976ca 100644
--- a/packages/safelight/src/components/Menu/Settings/Settings.vue
+++ b/packages/safelight/src/components/Menu/Settings/Settings.vue
@@ -1,6 +1,6 @@
-
+
From ecbc7c7b140b18a768d01d9a0020a4f0e134e087 Mon Sep 17 00:00:00 2001
From: Joery <44531907+Joery-M@users.noreply.github.com>
Date: Mon, 1 Jul 2024 22:14:53 +0200
Subject: [PATCH 15/18] ci(Workspace): :construction_worker: Added
dot-path-value to dependabot minor group
---
.github/dependabot.yml | 1 +
1 file changed, 1 insertion(+)
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index f645c827..3345a2f8 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -11,6 +11,7 @@ updates:
- "@endo/static-module-record"
- "@quantco/pnpm-licenses"
- "@tsconfig/node20"
+ - "dot-path-value"
- "fuzzysearch"
- "luxon"
- "mediainfo.js"
From aace3dfeae7c2ac4463f01d1cb6ebb267c1c0afe Mon Sep 17 00:00:00 2001
From: Joery <44531907+Joery-M@users.noreply.github.com>
Date: Mon, 1 Jul 2024 22:39:34 +0200
Subject: [PATCH 16/18] refactor(Settings): :building_construction: Moved
defaultNamespaces definition to setup method
This is to facilitate testing and configurability
---
packages/safelight/src/App.vue | 124 +++++++++++++++-
.../shared/src/Settings/SettingsManager.ts | 138 ++----------------
2 files changed, 134 insertions(+), 128 deletions(-)
diff --git a/packages/safelight/src/App.vue b/packages/safelight/src/App.vue
index eaa5a5b3..a3685bfa 100644
--- a/packages/safelight/src/App.vue
+++ b/packages/safelight/src/App.vue
@@ -8,7 +8,7 @@
import { SettingsManager } from '@safelight/shared/Settings/SettingsManager';
import { useTitle } from '@vueuse/core';
import DynamicDialog from 'primevue/dynamicdialog';
-import { watchEffect } from 'vue';
+import { defineAsyncComponent, markRaw, watchEffect } from 'vue';
import { RouterView } from 'vue-router';
import NotificationManager from './components/General/Notifications/NotificationManager.vue';
import { router } from './main';
@@ -28,5 +28,125 @@ watchEffect(() => {
}
});
-SettingsManager.setup();
+SettingsManager.setup([
+ {
+ name: 'general',
+ title: 'General',
+ childNamespaces: []
+ },
+ {
+ name: 'editor',
+ title: 'Editor',
+ icon: markRaw(
+ defineAsyncComponent(async () => (await import('@phosphor-icons/vue')).PhSidebar)
+ ),
+ childNamespaces: [
+ {
+ name: 'playback',
+ title: 'Playback',
+ icon: markRaw(
+ defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhPlayPause
+ )
+ ),
+ settings: []
+ },
+ {
+ name: 'timeline',
+ title: 'Timeline',
+ icon: markRaw(
+ defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhFilmStrip
+ )
+ ),
+ settings: [
+ {
+ type: 'boolean',
+ name: 'useTrackpad',
+ title: 'Trackpad mode',
+ description: 'Will inverse the axes on which the timeline will scroll.',
+ default: false
+ },
+ {
+ type: 'number',
+ name: 'zoomFactor',
+ title: 'Zoom factor',
+ description: 'The amount to zoom in and out by when scrolling.',
+ default: 2,
+ decimals: false,
+ range: true,
+ min: 1,
+ max: 100
+ },
+ {
+ type: 'enum',
+ name: 'align',
+ title: 'Align timeline',
+ default: 'bottom',
+ options: [
+ { value: 'top', label: 'Top' },
+ { value: 'bottom', label: 'Bottom' }
+ ],
+ labelKey: 'label',
+ valueKey: 'value'
+ }
+ ]
+ },
+ {
+ name: 'library',
+ title: 'Library',
+ icon: markRaw(
+ defineAsyncComponent(
+ async () => (await import('@phosphor-icons/vue')).PhFolders
+ )
+ ),
+ settings: [
+ {
+ type: 'group',
+ title: 'Media',
+ settings: [
+ {
+ type: 'string',
+ title: 'Test',
+ description: 'Test',
+ name: 'test',
+ pattern: /icle+/,
+ maxLength: 10,
+ default: 'icle'
+ }
+ ]
+ },
+ {
+ type: 'group',
+ title: 'Files',
+ settings: []
+ }
+ ]
+ }
+ ]
+ },
+ {
+ name: 'keyboard',
+ title: 'Keyboard',
+ icon: markRaw(
+ defineAsyncComponent(async () => (await import('@phosphor-icons/vue')).PhKeyboard)
+ ),
+ childNamespaces: [
+ {
+ name: 'hotkeys',
+ title: 'Hotkeys',
+ settings: [
+ {
+ type: 'custom',
+ name: 'keybinds',
+ title: 'Hotkeys',
+ component: markRaw(
+ defineAsyncComponent(() => import('@/views/dev/Packages.vue'))
+ )
+ }
+ ]
+ }
+ ]
+ }
+]);
diff --git a/packages/shared/src/Settings/SettingsManager.ts b/packages/shared/src/Settings/SettingsManager.ts
index 20c4f2f1..ba1e8458 100644
--- a/packages/shared/src/Settings/SettingsManager.ts
+++ b/packages/shared/src/Settings/SettingsManager.ts
@@ -3,7 +3,6 @@ import { useDialog } from 'primevue/usedialog';
import {
computed,
defineAsyncComponent,
- markRaw,
reactive,
toRaw,
type Component,
@@ -12,129 +11,7 @@ import {
} from 'vue';
export class SettingsManager {
- private static defaultNamespaces: SettingsNamespaceDefinition[] = [
- {
- name: 'general',
- title: 'General',
- childNamespaces: []
- },
- {
- name: 'editor',
- title: 'Editor',
- icon: markRaw(
- defineAsyncComponent(async () => (await import('@phosphor-icons/vue')).PhSidebar)
- ),
- childNamespaces: [
- {
- name: 'playback',
- title: 'Playback',
- icon: markRaw(
- defineAsyncComponent(
- async () => (await import('@phosphor-icons/vue')).PhPlayPause
- )
- ),
- settings: []
- },
- {
- name: 'timeline',
- title: 'Timeline',
- icon: markRaw(
- defineAsyncComponent(
- async () => (await import('@phosphor-icons/vue')).PhFilmStrip
- )
- ),
- settings: [
- {
- type: 'boolean',
- name: 'useTrackpad',
- title: 'Trackpad mode',
- description: 'Will inverse the axes on which the timeline will scroll.',
- default: false
- },
- {
- type: 'number',
- name: 'zoomFactor',
- title: 'Zoom factor',
- description: 'The amount to zoom in and out by when scrolling.',
- default: 2,
- decimals: false,
- range: true,
- min: 1,
- max: 100
- },
- {
- type: 'enum',
- name: 'align',
- title: 'Align timeline',
- default: 'bottom',
- options: [
- { value: 'top', label: 'Top' },
- { value: 'bottom', label: 'Bottom' }
- ],
- labelKey: 'label',
- valueKey: 'value'
- }
- ]
- },
- {
- name: 'library',
- title: 'Library',
- icon: markRaw(
- defineAsyncComponent(
- async () => (await import('@phosphor-icons/vue')).PhFolders
- )
- ),
- settings: [
- {
- type: 'group',
- title: 'Media',
- settings: [
- {
- type: 'string',
- title: 'Test',
- description: 'Test',
- name: 'test',
- pattern: /icle+/,
- maxLength: 10,
- default: 'icle'
- }
- ]
- },
- {
- type: 'group',
- title: 'Files',
- settings: []
- }
- ]
- }
- ]
- },
- {
- name: 'keyboard',
- title: 'Keyboard',
- icon: markRaw(
- defineAsyncComponent(async () => (await import('@phosphor-icons/vue')).PhKeyboard)
- ),
- childNamespaces: [
- {
- name: 'hotkeys',
- title: 'Hotkeys',
- settings: [
- {
- type: 'custom',
- name: 'keybinds',
- title: 'Hotkeys',
- component: markRaw(
- defineAsyncComponent(
- () => import('../../../safelight/src/views/dev/Packages.vue')
- )
- )
- }
- ]
- }
- ]
- }
- ];
+ private static defaultNamespaces: SettingsNamespaceDefinition[] = [];
private static saveTimeout: ReturnType
;
@@ -146,7 +23,15 @@ export class SettingsManager {
private static defaultsCreated = false;
- public static setup() {
+ public static setup(defaultNamespaces: SettingsNamespaceDefinition[]) {
+ this.defaultNamespaces = defaultNamespaces;
+
+ Object.assign(this.currentSettings, {});
+ Object.assign(this.defaultSettings, {});
+ this.settingsDefinition.clear();
+ this.defaultsCreated = false;
+ clearTimeout(this.saveTimeout);
+
this.createDefaultNamespaces(this.defaultNamespaces, []);
this.createDefaultSettings(Array.from(this.settingsDefinition.values()));
@@ -171,7 +56,7 @@ export class SettingsManager {
return pathArray.reduce((ns, path, curIndex) => {
if (!ns) return;
- const childNs = ns?.childNamespaces.find(
+ const childNs = ns?.childNamespaces?.find(
(ns) => ns.path == pathArray.slice(0, curIndex + 1).join('.')
);
@@ -246,6 +131,7 @@ export class SettingsManager {
}
public static saveSettings() {
+ clearInterval(this.saveTimeout);
localStorage.setItem('sl-settings', JSON.stringify(toRaw(this.currentSettings)));
}
From 2df2c57e4cc1a7d9bc27586edea6ff11cb966a60 Mon Sep 17 00:00:00 2001
From: Joery <44531907+Joery-M@users.noreply.github.com>
Date: Mon, 1 Jul 2024 22:39:58 +0200
Subject: [PATCH 17/18] test(Settings): :test_tube: Added tests for Settings
---
.../shared/test/Settings/Settings.spec.ts | 70 +++++++++++++++++++
.../test/Settings/SettingsNamespaces.spec.ts | 40 +++++++++++
2 files changed, 110 insertions(+)
create mode 100644 packages/shared/test/Settings/Settings.spec.ts
create mode 100644 packages/shared/test/Settings/SettingsNamespaces.spec.ts
diff --git a/packages/shared/test/Settings/Settings.spec.ts b/packages/shared/test/Settings/Settings.spec.ts
new file mode 100644
index 00000000..a94a6c24
--- /dev/null
+++ b/packages/shared/test/Settings/Settings.spec.ts
@@ -0,0 +1,70 @@
+import { beforeEach, expect, test } from 'vitest';
+import { SettingsManager } from '../../src/Settings/SettingsManager';
+
+beforeEach(() => {
+ localStorage.clear();
+ SettingsManager.setup([
+ {
+ name: 'test',
+ title: 'Test',
+ settings: [
+ {
+ type: 'boolean',
+ name: 'onOff',
+ title: 'Lightswitch',
+ default: false
+ }
+ ]
+ }
+ ]);
+});
+
+test('Getting setting', () => {
+ const setting = SettingsManager.getSetting('test.onOff');
+
+ expect(setting).toBeDefined();
+
+ expect(setting.value).toBe(false);
+});
+
+test('Setting setting value', () => {
+ SettingsManager.setSetting('test.onOff', true);
+
+ const setting = SettingsManager.getSetting('test.onOff');
+
+ expect(setting).toBeDefined();
+
+ expect(setting.value).toBe(true);
+});
+
+test('Saving settings to localStorage', () => {
+ SettingsManager.setSetting('test.onOff', true);
+
+ const setting = SettingsManager.getSetting('test.onOff');
+
+ expect(setting).toBeDefined();
+
+ expect(setting.value).toBe(true);
+
+ SettingsManager.saveSettings();
+ // Get value from localstorage
+ const storedVal = JSON.parse(localStorage.getItem('sl-settings'));
+ expect(storedVal).toMatchObject({
+ test: {
+ onOff: true
+ }
+ });
+});
+test('Loading from localStorage', () => {
+ localStorage.setItem(
+ 'sl-settings',
+ JSON.stringify({
+ test: {
+ onOff: true
+ }
+ })
+ );
+ SettingsManager.loadSettings();
+ const setting = SettingsManager.getSetting('test.onOff');
+ expect(setting.value).toBe(true);
+});
diff --git a/packages/shared/test/Settings/SettingsNamespaces.spec.ts b/packages/shared/test/Settings/SettingsNamespaces.spec.ts
new file mode 100644
index 00000000..3086eab9
--- /dev/null
+++ b/packages/shared/test/Settings/SettingsNamespaces.spec.ts
@@ -0,0 +1,40 @@
+import { expect, test } from 'vitest';
+import { SettingsManager } from '../../src/Settings/SettingsManager';
+
+test('Assigning namespaces', () => {
+ SettingsManager.setup([
+ {
+ name: 'test',
+ title: 'Test',
+ settings: [
+ {
+ type: 'boolean',
+ name: 'onOff',
+ title: 'Lightswitch',
+ default: false
+ }
+ ]
+ }
+ ]);
+});
+
+test('Getting namespaces', () => {
+ SettingsManager.setup([
+ {
+ name: 'test',
+ title: 'Test',
+ settings: [
+ {
+ type: 'boolean',
+ name: 'onOff',
+ title: 'Lightswitch',
+ default: false
+ }
+ ]
+ }
+ ]);
+
+ expect(SettingsManager.getNamespace('test')).toBeDefined();
+ expect(SettingsManager.getNamespace('test').title).toBe('Test');
+ expect(SettingsManager.getNamespace('test').settings).toHaveLength(1);
+});
From edc3aa20ac501aeb1cbca55b45e421708b98f3e1 Mon Sep 17 00:00:00 2001
From: Joery <44531907+Joery-M@users.noreply.github.com>
Date: Mon, 1 Jul 2024 22:46:37 +0200
Subject: [PATCH 18/18] fix(Timeline): :green_heart: Fixed vue-tsc error from
timeline
---
packages/timeline/src/TimelineItemComponent.vue | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/packages/timeline/src/TimelineItemComponent.vue b/packages/timeline/src/TimelineItemComponent.vue
index c99b3ec8..ee0b915d 100644
--- a/packages/timeline/src/TimelineItemComponent.vue
+++ b/packages/timeline/src/TimelineItemComponent.vue
@@ -2,7 +2,7 @@