Skip to content

Commit

Permalink
Merge pull request #114 from Joery-M/37-sharedsl-ui-settings
Browse files Browse the repository at this point in the history
37 sharedsl UI settings
  • Loading branch information
Joery-M authored Jul 1, 2024
2 parents e0dd595 + edc3aa2 commit dc355b2
Show file tree
Hide file tree
Showing 28 changed files with 1,235 additions and 40 deletions.
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ updates:
- "@endo/static-module-record"
- "@quantco/pnpm-licenses"
- "@tsconfig/node20"
- "dot-path-value"
- "fuzzysearch"
- "luxon"
- "mediainfo.js"
Expand Down
1 change: 1 addition & 0 deletions packages/safelight/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"@vueuse/math": "^10.11.0",
"@vueuse/rxjs": "^10.11.0",
"dexie": "^4.0.7",
"dot-path-value": "^0.0.10",
"fuzzysearch": "^1.0.3",
"hash-wasm": "^4.11.0",
"luxon": "^3.4.4",
Expand Down
127 changes: 126 additions & 1 deletion packages/safelight/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
<template>
<RouterView />
<DynamicDialog />
<NotificationManager />
</template>

<script setup lang="ts">
import { SettingsManager } from '@safelight/shared/Settings/SettingsManager';
import { useTitle } from '@vueuse/core';
import { watchEffect } from 'vue';
import DynamicDialog from 'primevue/dynamicdialog';
import { defineAsyncComponent, markRaw, watchEffect } from 'vue';
import { RouterView } from 'vue-router';
import NotificationManager from './components/General/Notifications/NotificationManager.vue';
import { router } from './main';
Expand All @@ -24,4 +27,126 @@ watchEffect(() => {
pageTitle.value = 'Safelight';
}
});
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'))
)
}
]
}
]
}
]);
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@
v-model:items="items"
:playback-position="pbPos"
:fps="timeline?.framerate.value"
:invert-scroll-axes
:zoom-factor
:alignment
class="h-full"
@update:playback-position="setPbPos"
/>
</template>

<script setup lang="ts">
import { CurrentProject } from '@/stores/currentProject';
import { SettingsManager } from '@safelight/shared/Settings/SettingsManager';
import Timecode from '@safelight/shared/Timecode';
import { Timeline as SLTimeline, type TimelineItem } from '@safelight/timeline/source';
import { v4 as uuidv4 } from 'uuid';
Expand All @@ -23,6 +27,9 @@ const pbPos = computed(() =>
? Timecode.fromFrames(timeline.value.pbPos.value, timeline.value.framerate.value)
: 0
);
const invertScrollAxes = SettingsManager.getSetting<boolean>('editor.timeline.useTrackpad');
const zoomFactor = SettingsManager.getSetting<number>('editor.timeline.zoomFactor');
const alignment = SettingsManager.getSetting<'top' | 'bottom'>('editor.timeline.align');
function setPbPos(pb?: number) {
if (pb !== undefined && timeline?.value) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<div v-if="currentSettings" ref="scrollContainer" class="h-full flex-1 overflow-y-auto">
<h1 class="mt-0">
{{ currentSettings.title }}
</h1>
<vue-markdown
v-if="currentSettings.description"
:source="currentSettings.description"
:html="false"
/>
<div v-if="currentSettings.settings" role="list">
<SettingsGroup
:settings="currentSettings.settings"
:namespace="currentSettings.pathArray"
:is-first="true"
/>
</div>
</div>
</template>

<script setup lang="ts">
import { SettingsManager } from '@safelight/shared/Settings/SettingsManager';
import { computed, ref, watch } from 'vue';
import VueMarkdown from 'vue-markdown-render';
import SettingsGroup from './SettingsProperties/SettingsGroup.vue';
const props = withDefaults(defineProps<{ path: string }>(), {
path: () => 'general'
});
const scrollContainer = ref<HTMLDivElement>();
const currentSettings = computed(() => SettingsManager.getNamespace(props.path));
watch(currentSettings, () => {
if (scrollContainer.value) {
scrollContainer.value.scrollTo(0, 0);
}
});
</script>
126 changes: 126 additions & 0 deletions packages/safelight/src/components/Menu/Settings/Settings.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
<template>
<div class="flex h-full">
<div class="tree">
<Tree
v-model:expandedKeys="expandedKeys"
class="min-w-64"
:selection-keys="{ [selectedPath]: true }"
:value="tree"
selection-mode="single"
@node-select="onNodeSelect"
>
<template #nodeicon="{ node }">
<template v-if="node.data?.icon">
<component :is="node.data.icon" class="mr-2" />
</template>
</template>
</Tree>
<Button severity="secondary" @click="SettingsManager.downloadSettings()">
<PhDownload />
Download JSON
</Button>
</div>
<div
class="mr-4 border-solid"
style="
border-width: 0;
border-right-width: 1px;
border-right-color: var(--color-border);
"
/>
<NamespaceSettings class="namespace" :path="selectedPath" />
</div>
</template>

<script setup lang="ts">
import { PhDownload } from '@phosphor-icons/vue';
import { SettingsManager, SettingsNamespace } from '@safelight/shared/Settings/SettingsManager';
import Button from 'primevue/button';
import type { DynamicDialogInstance } from 'primevue/dynamicdialogoptions';
import Tree from 'primevue/tree';
import type { TreeNode } from 'primevue/treenode';
import { computed, inject, onMounted, ref, type Ref } from 'vue';
import NamespaceSettings from './NamespaceSettings.vue';
const selectedPath = ref<string>('general');
const expandedKeys = ref({});
const dialogRef = inject<Ref<DynamicDialogInstance>>('dialogRef');
onMounted(() => {
if (dialogRef?.value?.data && dialogRef.value.data.namespace) {
selectedPath.value = dialogRef.value.data.namespace;
const namespaces = selectedPath.value.split('.');
expandedKeys.value = {};
namespaces.forEach((ns) => {
expandedKeys.value = { ...expandedKeys.value, [ns]: true };
});
}
});
const tree = computed(() => {
return (Array.from(SettingsManager.settingsDefinition.values()) as SettingsNamespace[]).map(
(ns) => mapSettingsNs(ns)
);
});
function onNodeSelect(data: TreeNode) {
if (data.key) {
if ((SettingsManager.getNamespace(data.key)?.settings?.length ?? 0) > 0) {
selectedPath.value = data.key;
}
if (data.children && data.children.length > 0) {
expandedKeys.value = { [data.key]: true };
}
}
}
function mapSettingsNs(ns: SettingsNamespace): TreeNode {
return {
key: ns.path,
label: ns.title,
selectable: ns.settings?.length > 0 || ns.childNamespaces?.length > 0,
children: ns.childNamespaces?.map(mapSettingsNs),
data: {
icon: ns.icon
}
} as TreeNode;
}
</script>

<style lang="scss" scoped>
.tree {
@apply mr-4 flex flex-col;
> .p-tree {
@apply flex-1 overflow-y-auto;
}
> .p-button {
@apply flex w-full justify-center gap-2;
}
}
.namespace :deep(div[role='listitem']) {
@apply mb-4 pl-4;
h3 {
@apply -ml-2 mb-3 mt-0;
}
p {
@apply m-0;
}
.description {
line-height: normal;
}
a.default {
@apply invisible cursor-pointer align-middle text-sm font-extralight italic opacity-0 transition-all;
&.show {
@apply visible opacity-100;
}
> svg {
@apply align-middle;
}
}
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<template>
<h3 v-if="setting?.title">{{ setting.title }}</h3>
<vue-markdown v-if="setting?.description" :source="setting.description" />
</template>

<script lang="ts" setup>
import type { SettingsArrayProperty } from '@safelight/shared/Settings/SettingsManager';
import VueMarkdown from 'vue-markdown-render';
defineProps<{ namespace: string[]; setting: SettingsArrayProperty }>();
</script>
Loading

0 comments on commit dc355b2

Please sign in to comment.