Big update to deploy scripts, and deploy prosecss

This commit is contained in:
Max committed 2026-10-28 11:54:19 -07:00
1 parent 2d5d23b104
commit 590ae01055
141 files changed
+8483 -53810

No files matched your search

+143 -65
View File
@@ -117,7 +117,8 @@
</div>
<!-- render each section based on notes in set -->
<div v-for="section,index in noteSections" v-if="section.length > 0" class="note-card-section">
<template v-for="section,index in noteSections" :key="index">
<div v-if="section.length > 0" class="note-card-section">
<h5 class="ui tiny dividing header"><i :class="`green ${sectionData[index][0]} icon`"></i>{{ sectionData[index][1] }}</h5>
<div class="note-card-display-area">
@@ -133,6 +134,7 @@
/>
</div>
</div>
</template>
<div class="loading-section" v-if="showLoading">
@@ -163,20 +165,21 @@
<!-- flexbox note container evenly spaces open notes -->
<div class="note-panel-container" :class="{ 'note-panel-fullwidth':collapseFloatingList}" v-if="openNotes.length">
<note-input-panel
v-for="noteId in openNotes"
<template v-for="noteId in openNotes" :key="noteId">
<note-input-panel
v-if="noteId != null"
:key="noteId"
:noteid="noteId"
:url-data="$route.params"
:open-notes="openNotes.length"
/>
</template>
</div>
</div>
</template>
<script>
import { defineAsyncComponent } from 'vue'
import axios from 'axios'
@@ -184,15 +187,15 @@
name: 'NotesPage',
components: {
'note-input-panel': () => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue'),
'note-input-panel': defineAsyncComponent(() => import(/* webpackChunkName: "NoteInputPanel" */ '@/components/NoteInputPanel.vue')),
'note-title-display-card': require('@/components/NoteTitleDisplayCard.vue').default,
// 'fast-filters': require('@/components/FastFilters.vue').default,
'search-input': require('@/components/SearchInput.vue').default,
'attachment-display': require('@/components/AttachmentDisplayCard').default,
'tag-display':require('@/components/TagDisplayComponent.vue').default,
'loading-icon':require('@/components/LoadingIconComponent.vue').default,
'paste-button':require('@/components/PasteButton.vue').default,
'note-title-display-card': defineAsyncComponent(() => import('@/components/NoteTitleDisplayCard.vue')),
// 'fast-filters': defineAsyncComponent(() => import('@/components/FastFilters.vue')),
'search-input': defineAsyncComponent(() => import('@/components/SearchInput.vue')),
'attachment-display': defineAsyncComponent(() => import('@/components/AttachmentDisplayCard.vue')),
'tag-display':defineAsyncComponent(() => import('@/components/TagDisplayComponent.vue')),
'loading-icon':defineAsyncComponent(() => import('@/components/LoadingIconComponent.vue')),
'paste-button':defineAsyncComponent(() => import('@/components/PasteButton.vue')),
},
data () {
return {
@@ -203,6 +206,9 @@
searchTags: [],
notes: [],
openNotes: [],
// fullPath of our own last URL write, so the $route watcher can
// recognise it and not treat it as an inbound navigation
lastSyncedFullPath: null,
collapseFloatingList: false,
highlights: [],
searchDebounce: null,
@@ -258,7 +264,7 @@
},
beforeMount(){
this.$parent.loginGateway()
this.$root.loginGateway()
//If user is on title view,
this.titleView = this.$store.getters.getIsUserOnMobile
@@ -363,7 +369,7 @@
// document.addEventListener('visibilitychange', this.visibiltyChangeAction);
},
beforeDestroy(){
beforeUnmount(){
window.removeEventListener('scroll', this.onScroll)
// document.removeEventListener('visibilitychange', this.visibiltyChangeAction)
@@ -380,11 +386,26 @@
},
mounted() {
//Open note on PAGE LOAD if ID is set
if(this.$route.params.id > 1){
this.openNote(this.$route.params.id)
// Restore every note named in ?open=
const ids = this.parseOpenQuery(this.$route.query.open)
// Fold the legacy /notes/open/:id deep link in, first position
const paramId = parseInt(this.$route.params.id, 10)
if(Number.isInteger(paramId) && paramId > 1 && !ids.includes(paramId)){
ids.unshift(paramId)
}
// Push directly rather than via openNote(), which would write the URL
// once per note; one normalising write follows instead.
ids.forEach(id => {
if(!this.openNotes.includes(id)){
this.openNotes.push(id)
}
})
// Unconditional, so a junk ?open=abc gets scrubbed too
this.syncOpenNotesToUrl()
//Loads initial batch and tags
this.reset()
@@ -395,17 +416,31 @@
},
'$route' (to, from) {
// Our own URL write, not an inbound navigation
if(to.fullPath === this.lastSyncedFullPath){ return }
// Reload the notes if returning to this page
if(to.fullPath == '/notes' && !from.fullPath.includes('/notes/open/')){
// The discriminator is the absence of ?open=, NOT the path. A menu's
// $router.go(-1) lands on /notes?open=5 with `from` containing
// '/notes/open/', so a path comparison here would close every note
// every time a side menu is dismissed.
const landingOnBareNotes = (to.path === '/notes' && !to.query.open)
// Reload the notes if arriving at an empty /notes from elsewhere
if(landingOnBareNotes && !from.path.startsWith('/notes/open/') && from.path !== '/notes'){
this.reset()
}
// Close all notes if returning to /notes page
if(to.fullPath == '/notes' && from.fullPath.includes('/notes/open/')){
// Explicit navigation to a note-less /notes closes everything
if(landingOnBareNotes && this.openNotes.length > 0){
this.closeAllNotes()
}
// Stale history entry (e.g. go(-1) after another note was closed):
// rewrite the URL from the array. Never read it.
if(to.path === '/notes' && to.query.open && to.query.open !== this.openNotes.join(',')){
this.syncOpenNotesToUrl()
}
//Lookup tags set in URL
if(to.params.tag && this.$store.getters.totals && this.$store.getters.totals['tags'][to.params.tag]){
@@ -432,81 +467,121 @@
}
},
methods: {
openNote(id, event = null){
// Where the open-note URL should point. A menu or a tag search owns the
// path, so only a plain notes view gets normalised back to /notes.
openNotesTargetPath(){
//
// A side menu is open; its close button is a $router.go(-1), so the
// path has to stay put or there is nowhere sane to land.
if(this.$route.params.openMenu){ return this.$route.path }
const intId = parseInt(id)
if(this.openNotes.includes(intId)){
// Preserve a tag search
if(this.$route.params.tag){ return this.$route.path }
console.log('Open already open note?')
return '/notes'
},
// Mirror openNotes into ?open=. This is the ONLY place the open-note
// URL is written.
syncOpenNotesToUrl(){
// const openIndex = this.openNotes.indexOf(intId)
// if(openIndex != -1){
// console.log('Open note and remove it ', intId + ' on index ' + openIndex)
// this.openNotes.splice(openIndex, 1)
// }
// this.$bus.$emit('close_note_by_id', intId)
const query = Object.assign({}, this.$route.query)
if(this.openNotes.length > 0){
query.open = this.openNotes.join(',')
}
else {
delete query.open
}
const target = { path: this.openNotesTargetPath(), query }
const targetFullPath = this.$router.resolve(target).fullPath
// Already correct -- don't navigate at all
if(targetFullPath === this.$route.fullPath){
this.lastSyncedFullPath = targetFullPath
return
}
// Set before navigating so the watcher sees it
this.lastSyncedFullPath = targetFullPath
this.$router.replace(target).catch(() => {})
},
// Tolerant parse of ?open=. Handles 'abc', '1,,2,2', '-4' and the
// repeated form ?open=1&open=2 (which arrives as an array).
parseOpenQuery(raw){
if(raw === undefined || raw === null){ return [] }
// Cap: a hand-crafted URL shouldn't be able to fire 50 note fetches
const MAX_RESTORE = 8
return [].concat(raw)
.join(',')
.split(',')
.map(value => parseInt(value, 10))
.filter(value => Number.isInteger(value) && value > 0)
.filter((value, index, all) => all.indexOf(value) === index)
.slice(0, MAX_RESTORE)
},
openNote(id, event = null){
const intId = parseInt(id, 10)
// Also absorbs the undefined the '$route.params.id' watcher now gets
// when we normalise /notes/open/5 into /notes?open=5
if(!Number.isInteger(intId) || intId < 1){ return }
//Don't open note if a link is clicked in display card
if(event && event.target && event.target.nodeName){
const nodeClick = event.target.nodeName
if(nodeClick == 'A'){ return }
}
// Push note to stack if not open
if(Number.isInteger(intId) && !this.openNotes.includes(intId)){
this.openNotes.push(intId)
if(this.openNotes.includes(intId)){
// Already open, but an external push('/notes/open/5') still needs
// the URL normalised back to the full set
this.syncOpenNotesToUrl()
return
}
this.$nextTick(() => {
// change route if open ID is not the same as current ID
if(this.$route.params.id != id){
console.log('Open note, change route -> route id ' + this.$route.params.id + ' note id ->' + id + ', ' +(this.$route.params.id == id))
this.$router.push('/notes/open/'+id)
}
})
return
this.openNotes.push(intId)
this.syncOpenNotesToUrl()
},
closeNote(noteId, modified){
closeNote(noteId, modified, syncUrl = true){
console.log('close note', this.$route.fullPath)
// parseInt: indexOf is strict, and ids arriving from the URL are
// strings. Without this the splice silently misses.
const intId = parseInt(noteId, 10)
const openIndex = this.openNotes.indexOf(intId)
const didClose = (openIndex !== -1)
const openIndex = this.openNotes.indexOf(noteId)
if(openIndex != -1){
console.log('Removing note id ', noteId + ' on index ' + openIndex)
if(didClose){
this.openNotes.splice(openIndex, 1)
}
// //A note has been closed
// if(this.$route.fullPath != '/notes'){
// this.$router.push('/notes')
// }
if(this.openNotes.length == 0 && this.$route.fullPath != '/notes'){
this.$router.push('/notes')
// Only write the URL when the array actually changed. NoteInputPanel
// emits close_active_note twice per note (closeButtonAction -> close(),
// then beforeUnmount -> close()); the second arrives after the splice.
if(didClose && syncUrl){
this.syncOpenNotesToUrl()
}
if(modified){
console.log('Just closed Note -> ' + noteId + ', modified -> ', modified)
this.$store.dispatch('fetchAndUpdateUserTotals')
//Focus and animate if modified
this.updateSingleNote(noteId, modified)
}
console.log('closeNote(): Open notes length ', this.openNotes.length)
},
closeAllNotes(){
console.log('Close all notes ------------')
// Backwards, so the in-loop splice can't skip an entry. URL writes are
// deferred so this is one navigation rather than N.
for (let i = this.openNotes.length - 1; i >= 0; i--) {
console.log('Close all notes -> ' + this.openNotes[i])
this.closeNote(this.openNotes[i])
this.closeNote(this.openNotes[i], false, false)
}
console.log('----------------')
this.syncOpenNotesToUrl()
},
toggleTagFilter(tagId){
@@ -741,7 +816,10 @@
return resolve(true)
})
.catch(error => { this.$bus.$emit('notification', 'Failed to Search Notes') })
.catch(error => {
console.error('[NotesPage] search failed:', error)
this.$bus.$emit('notification', 'Failed to Search Notes')
})
})
},
rebuildNoteCategorise(){