From 158d9a15164d407909215b65975ff7d6e981d0a1 Mon Sep 17 00:00:00 2001 From: Danny Coulombe Date: Fri, 19 Dec 2025 09:38:27 -0500 Subject: [PATCH 1/4] Dark mode --- src/assets/logo-dark.png | Bin 0 -> 5405 bytes src/components/FieldItem.vue | 12 ++++++++++++ src/components/JSONms.vue | 10 ++++++++++ src/components/Logo.vue | 11 ++++++++++- src/components/Sidebar.vue | 2 +- src/components/SitePreview.vue | 2 +- src/components/UserSettingsDialog.vue | 13 +++++++++++++ src/interfaces.ts | 1 + src/plugins/vuetify.ts | 4 +++- src/stores/global.ts | 1 + 10 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 src/assets/logo-dark.png diff --git a/src/assets/logo-dark.png b/src/assets/logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..c8a2d3b982f2dd60be9ad424d3bbf1b4997ca698 GIT binary patch literal 5405 zcmV+&72@iNP)z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy24YJ`L;wH)E&%`Ll0O�SaNLh0L01m?d01m?e$8V@)00007 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 Hu0mjflP^c) literal 0 HcmV?d00001 diff --git a/src/components/FieldItem.vue b/src/components/FieldItem.vue index cb3f062..753a91d 100644 --- a/src/components/FieldItem.vue +++ b/src/components/FieldItem.vue @@ -922,4 +922,16 @@ watch(() => field.collapsed, () => { .handle { cursor: grabbing; } +.v-theme--dark { + .CodeMirror { + background-color: rgb(var(--v-theme-sheet)); + color: inherit; + } + .CodeMirror-cursor { + border-left: white solid 1px; + } + .editor-toolbar button:hover { + color: black; + } +} diff --git a/src/components/JSONms.vue b/src/components/JSONms.vue index 0d5961d..96aac9d 100644 --- a/src/components/JSONms.vue +++ b/src/components/JSONms.vue @@ -26,6 +26,7 @@ import FileManager from '@/components/FileManager.vue'; import {useModelStore} from '@/stores/model'; import structureMd from "../../docs/structure.md"; import {useSyncing} from "@/composables/syncing"; +import { useTheme } from 'vuetify' // Model & Props const structure = defineModel({ required: true }); @@ -262,6 +263,15 @@ window.addEventListener('fs-change', () => { syncFromFolder(structure.value); }) +const theme = useTheme(); +const toggleDarkMode = () => { + theme.global.name.value = globalStore.userSettings.data.appearanceDarkMode ? 'dark' : 'light' +} +watch(() => [ + globalStore.userSettings.data.appearanceDarkMode, +], toggleDarkMode); +toggleDarkMode(); + const refreshUserData = async (): Promise => { return fetchUserData().then((response: any) => { setUserData(response.data, true); diff --git a/src/components/Logo.vue b/src/components/Logo.vue index f783f88..72b51ca 100644 --- a/src/components/Logo.vue +++ b/src/components/Logo.vue @@ -1,5 +1,14 @@ diff --git a/src/components/SitePreview.vue b/src/components/SitePreview.vue index 765c414..b4f1abc 100644 --- a/src/components/SitePreview.vue +++ b/src/components/SitePreview.vue @@ -166,10 +166,10 @@ defineExpose({ > visible.value, () => { + + + + + + Date: Fri, 19 Dec 2025 18:35:23 -0500 Subject: [PATCH 2/4] when cleaning, save button does not activate --- package.json | 2 +- src/components/JSONms.vue | 12 ++++++++---- src/components/JsonEditModal.vue | 4 ++-- src/composables/user-data.ts | 5 ++--- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index e2e223a..786aefd 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@json.ms/www", "private": true, "type": "module", - "version": "1.3.0", + "version": "1.3.1", "scripts": { "dev": "vite --host", "build": "run-p type-check \"build-only {@}\" --", diff --git a/src/components/JSONms.vue b/src/components/JSONms.vue index 96aac9d..181ac5f 100644 --- a/src/components/JSONms.vue +++ b/src/components/JSONms.vue @@ -209,14 +209,18 @@ const onEditJson = () => { editJsonContent.value = JSON.stringify(deepToRaw(modelStore.userData), null, globalStore.userSettings.data.editorTabSize); } -const onMigrateData = () => { - showMigrationDialog.value = true; +const onCleanJson = (data: any) => { + editJsonContent.value = JSON.stringify(data || {}, null, globalStore.userSettings.data.editorTabSize); } const onApplyJsonContent = (json: any) => { setUserData(json); } +const onMigrateData = () => { + showMigrationDialog.value = true; +} + let oldModel: IStructure | null = null; const onStructureChange = (model: IStructure) => { if (oldModel) { @@ -265,7 +269,7 @@ window.addEventListener('fs-change', () => { const theme = useTheme(); const toggleDarkMode = () => { - theme.global.name.value = globalStore.userSettings.data.appearanceDarkMode ? 'dark' : 'light' + theme.change(globalStore.userSettings.data.appearanceDarkMode ? 'dark' : 'light'); } watch(() => [ globalStore.userSettings.data.appearanceDarkMode, @@ -496,7 +500,7 @@ if (globalStore.session.loggedIn) { v-model="editJsonContent" v-model:visible="showEditJson" @apply="onApplyJsonContent" - @clean="onEditJson" + @clean="onCleanJson" /> diff --git a/src/components/JsonEditModal.vue b/src/components/JsonEditModal.vue index 8fc9bca..9958e32 100644 --- a/src/components/JsonEditModal.vue +++ b/src/components/JsonEditModal.vue @@ -47,8 +47,8 @@ const onCleanUserData = () => { btnIcon: 'mdi-vacuum-outline', btnColor: 'warning', callback: () => new Promise(resolve => { - cleanUserData() - emit('clean'); + const data = cleanUserData() + emit('clean', data); resolve(); }) }) diff --git a/src/composables/user-data.ts b/src/composables/user-data.ts index 37ac7ab..5b7333d 100644 --- a/src/composables/user-data.ts +++ b/src/composables/user-data.ts @@ -281,10 +281,9 @@ export function useUserData() { }) } - const cleanUserData = () => { + const cleanUserData = (): any => { const parsedStructure = getParsedStructureData(modelStore.structure); - const data = getParsedUserData(parsedStructure, modelStore.userData, true); - setUserData(data, true); + return getParsedUserData(parsedStructure, modelStore.userData, true); } const setUserData = (data: any, setOriginal = false) => { From c12ff44db8ec918d7376f2b15e69727daa3daa2b Mon Sep 17 00:00:00 2001 From: Danny Coulombe Date: Fri, 19 Dec 2025 18:35:34 -0500 Subject: [PATCH 3/4] add typings --- src/directives/if-ref.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/directives/if-ref.ts b/src/directives/if-ref.ts index 73dbe9e..6997ab4 100644 --- a/src/directives/if-ref.ts +++ b/src/directives/if-ref.ts @@ -1,29 +1,33 @@ +import type { Directive, DirectiveBinding } from 'vue'; + +type CleanupEl = HTMLElement & { + _cleanup?: () => void; +}; + const placeholder = document.createComment('v-if-ref'); -const checkCondition = (el, binding) => { +const checkCondition = (el: CleanupEl, binding: DirectiveBinding): void => { if (el.parentNode && !binding.value) { el.parentNode.insertBefore(placeholder, el); el.remove(); } else if (!el.parentNode && binding.value) { - placeholder.parentNode.insertBefore(el, placeholder); + placeholder.parentNode?.insertBefore(el, placeholder); placeholder.remove(); } }; -const vIfRef = { - mounted(el, binding) { +const vIfRef: Directive = { + mounted(el: CleanupEl, binding: DirectiveBinding) { checkCondition(el, binding); const handleResize = () => checkCondition(el, binding); window.addEventListener('resize', handleResize); el._cleanup = () => window.removeEventListener('resize', handleResize); }, - updated(el, binding) { + updated(el: CleanupEl, binding: DirectiveBinding) { console.log(binding); checkCondition(el, binding); }, - beforeUnmount(el) { - if (el._cleanup) { - el._cleanup(); - } + beforeUnmount(el: CleanupEl) { + el._cleanup?.(); } }; From b00abf0cf4ab7937451559abba3099590434d19b Mon Sep 17 00:00:00 2001 From: Danny Coulombe Date: Fri, 19 Dec 2025 21:36:08 -0500 Subject: [PATCH 4/4] set as default values error --- src/components/ActionBar.vue | 5 +- src/components/StructureEditor.vue | 126 +++++++++++++++++++++++++---- 2 files changed, 114 insertions(+), 17 deletions(-) diff --git a/src/components/ActionBar.vue b/src/components/ActionBar.vue index d8b38f1..4da182f 100644 --- a/src/components/ActionBar.vue +++ b/src/components/ActionBar.vue @@ -19,7 +19,7 @@ const globalStore = useGlobalStore(); const modelStore = useModelStore(); const { structureIsPristine, setDefaultValues } = useStructure(); const { windowWidth, layoutSize } = useLayout(); -const { saveUserData, resetUserData, userDataHasChanged, canInteractWithServer, canInteractWithSyncedFolder, userDataSaving, userDataSaved, canSave } = useUserData(); +const { saveUserData, resetUserData, cleanUserData, userDataHasChanged, canInteractWithServer, canInteractWithSyncedFolder, userDataSaving, userDataSaved, canSave } = useUserData(); const saveMaxWidth = computed((): string => { const long = canInteractWithServer.value && canInteractWithSyncedFolder.value; @@ -38,7 +38,8 @@ const onSetAsDefaultValues = () => { btnIcon: 'mdi-text-box-check-outline', btnColor: 'warning', callback: () => new Promise(resolve => { - setDefaultValues(modelStore.structure, modelStore.userData); + const cleanData = cleanUserData(); + setDefaultValues(modelStore.structure, cleanData); resolve(); }) }) diff --git a/src/components/StructureEditor.vue b/src/components/StructureEditor.vue index f9d3d05..b5f6ea7 100644 --- a/src/components/StructureEditor.vue +++ b/src/components/StructureEditor.vue @@ -12,7 +12,7 @@ import {useGlobalStore} from '@/stores/global'; import {useTypings} from "@/composables/typings"; import {useModelStore} from "@/stores/model"; import {useSyncing} from "@/composables/syncing"; -import {useUserData} from "@/composables/user-data"; +import {LineCounter, parseDocument} from "yaml"; // import yaml from 'js-yaml'; const emit = defineEmits(['save', 'create', 'change', 'focus', 'blur']); @@ -29,6 +29,13 @@ const { isFolderSynced, askToSyncFolder, unSyncFolder, lastStateTimestamp } = us const showParsingDelay = ref(false); const progressBarValue = ref(0); const progressBarCompleted = ref(false); +const animateAnnotationWarning = ref(false); +const annotations: Ref<{ + row: number + column: number + text: string + type: string +}[]> = ref([]); const globalStore = useGlobalStore(); const structureEditor: Ref = ref(null); const blueprintEditorTypings: Ref = ref(null); @@ -111,7 +118,10 @@ const value = computed({ set(value: string) { modelStore.setTemporaryContent(value); if (globalStore.userSettings.data.editorLiveUpdate) { - emit('change', value); + printAnnotations(value); + if (annotations.value.length === 0) { + emit('change', value); + } } else { clearInterval(parseInterval); clearInterval(showParseInterval); @@ -124,7 +134,10 @@ const value = computed({ parseValueInterval = setTimeout(() => progressBarValue.value = 100, 100); parseInterval = setTimeout(() => { - emit('change', value); + printAnnotations(value); + if (annotations.value.length === 0) { + emit('change', value); + } progressBarCompleted.value = true; setTimeout(() => showParsingDelay.value = true); setTimeout(() => { @@ -177,21 +190,60 @@ const onBlur = () => { emit('blur'); } +const printAnnotations = (content: string) => { + const instance = structureEditor.value?.getAceInstance(); + if (instance) { + animateAnnotationWarning.value = false; + annotations.value = []; + const lineCounter = new LineCounter(); + const doc = parseDocument(content, {lineCounter}); + doc.errors.forEach(error => { + const line = error.linePos?.[0].line; + if (line) { + annotations.value.push({ + row: line - 1, + column: 0, + text: error.message, + type: "error" + }); + } + }) + doc.warnings.forEach(error => { + const line = error.linePos?.[0].line; + if (line) { + annotations.value.push({ + row: line - 1, + column: 0, + text: error.message, + type: "warning" + }); + } + }) + instance.session.setAnnotations(annotations.value); + + if (annotations.value.length > 0) { + animateAnnotationWarning.value = true; + setTimeout(() => { + animateAnnotationWarning.value = false; + }, 200); + } + } +} + +const goToNextAnnotation = () => { + const instance = structureEditor.value?.getAceInstance(); + if (instance && annotations.value.length > 0) { + instance.renderer.scrollToLine(annotations.value[0].row, false, true); + } +} + const scrollToSection = (section: string) => { const instance = structureEditor.value?.getAceInstance(); if (instance) { - const annotations = []; const line = findNeedleInString(structure.value.content, ' ' + section + ':'); if (line) { instance.renderer.scrollToLine(line - 1, false, true); - annotations.push({ - row: line - 1, - column: 0, - text: "Current section", - type: "info" - }); } - instance.session.setAnnotations(annotations); } } @@ -257,6 +309,7 @@ const tryToAddCommands = () => { onMounted(() => { tryToAddCommands(); onChange(); + printAnnotations(structure.value.content); }) onBeforeUnmount(() => { // editor.value?.destroy(); @@ -356,7 +409,7 @@ defineExpose({ function onChange() { if (structureEditor.value) { -// updateAnnotations(editor.value?.getAceInstance()); + } } @@ -435,6 +488,9 @@ const structureOptions = computed(() => { return { ...options.value, tabSize: 2 }; }); +watch(() => structure.value.hash, () => { + printAnnotations(structure.value.content); +}); watch(() => globalStore.userSettings.data, () => { options.value.fontSize = globalStore.userSettings.data.editorFontSize; options.value.tabSize = globalStore.userSettings.data.editorTabSize; @@ -487,9 +543,33 @@ watch(() => globalStore.userSettings.data, () => { /> + +
+ + + + +
globalStore.userSettings.data, () => { @@ -684,4 +764,20 @@ watch(() => globalStore.userSettings.data, () => { .v-window ::v-deep .v-window__container { height: 100% !important; } +.annotation-warning { + &.animate { + animation: pulse-once 200ms ease-out normal; + } +} +@keyframes pulse-once { + 0% { + transform: scale(1); + } + 50% { + transform: scale(1.5); + } + 100% { + transform: scale(1); + } +}