add notes directory and update note handling in NotesService

This commit is contained in:
2026-03-07 02:59:13 +01:00
parent 883eec3e4d
commit 8872bddc55
6 changed files with 76 additions and 78 deletions
@@ -0,0 +1,3 @@
{
"editor.formatOnSave": false
}
@@ -36,6 +36,7 @@ Singleton {
property color distro: "#74c7ec" property color distro: "#74c7ec"
property color transparent: "#00000000" property color transparent: "#00000000"
readonly property var cavaList: [mLavender, mBlue, mSky, mCyan, mGreen, mYellow, mOrange, mRed] readonly property var cavaList: [mLavender, mBlue, mSky, mCyan, mGreen, mYellow, mOrange, mRed]
readonly property var noteList: [mLavender, mBlue, mSky, mCyan, mGreen, mYellow, mOrange, mRed, mPurple, mPink]
function reloadColors(newColors) { function reloadColors(newColors) {
if (typeof newColors === "string") { if (typeof newColors === "string") {
@@ -7,5 +7,6 @@ Singleton {
readonly property string cacheDir: Quickshell.shellDir + "/Assets/Cache/" readonly property string cacheDir: Quickshell.shellDir + "/Assets/Cache/"
readonly property string configDir: Quickshell.shellDir + "/Assets/Config/" readonly property string configDir: Quickshell.shellDir + "/Assets/Config/"
readonly property string notesDir: Quickshell.env("HOME") + "/Documents/qs-notes/"
readonly property string recordingDir: Quickshell.env("HOME") + "/Videos/recordings/" readonly property string recordingDir: Quickshell.env("HOME") + "/Videos/recordings/"
} }
@@ -87,7 +87,7 @@ Rectangle {
model: NotesService.notesModel model: NotesService.notesModel
delegate: UBox { delegate: UBox {
property color accentColor: Colors.cavaList[model.colorIdx % Colors.cavaList.length] property color accentColor: Colors.noteList[model.colorIdx % Colors.noteList.length]
width: notesColumn.width width: notesColumn.width
implicitHeight: noteLayout.implicitHeight + Style.marginM * 2 implicitHeight: noteLayout.implicitHeight + Style.marginM * 2
@@ -104,13 +104,13 @@ Rectangle {
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
acceptedButtons: Qt.LeftButton acceptedButtons: Qt.LeftButton
onClicked: NotesService.openNote(model.noteId) onClicked: NotesService.openNote(model.notePath)
} }
FileView { FileView {
id: fileView id: fileView
path: NotesService.notesDir + "/" + model.noteId + ".txt" path: model.notePath
watchChanges: true watchChanges: true
onFileChanged: reload() onFileChanged: reload()
} }
@@ -140,7 +140,7 @@ Rectangle {
iconName: "trash" iconName: "trash"
baseSize: Style.baseWidgetSize * 0.8 baseSize: Style.baseWidgetSize * 0.8
colorFg: Colors.mError colorFg: Colors.mError
onClicked: NotesService.deleteNote(model.noteId) onClicked: NotesService.deleteNote(model.notePath)
} }
} }
@@ -12,7 +12,7 @@ Singleton {
Component.onCompleted: { Component.onCompleted: {
let mkdirs = ""; let mkdirs = "";
for (const dir of [Paths.cacheDir, Paths.configDir, Paths.recordingDir]) { for (const dir of [Paths.cacheDir, Paths.configDir, Paths.recordingDir, Paths.notesDir]) {
mkdirs += `mkdir -p "${dir}" && `; mkdirs += `mkdir -p "${dir}" && `;
} }
mkdirs += "true"; mkdirs += "true";
@@ -8,119 +8,112 @@ pragma Singleton
Singleton { Singleton {
id: root id: root
property string notesDir: Paths.cacheDir + "/notes"
property var notes: []
property ListModel notesModel property ListModel notesModel
function loadNotes() {
listProcess.running = true;
}
function createNote() { function createNote() {
var id = new Date().getTime().toString(); if (createProcess.running) {
var filePath = notesDir + "/" + id + ".txt"; Logger.w("Notes", "Create process is already running, skipping new note creation.");
// Random color index from 0 to 7 return ;
var colorIdx = Math.floor(Math.random() * 8); }
createProcess.command = ["sh", "-c", "mkdir -p " + notesDir + " && echo 'New Note' > " + filePath + " && echo " + colorIdx + " > " + filePath + ".color"]; const fileName = generateFileName();
const path = Paths.notesDir + "/" + fileName;
createProcess.currentNote = {
"notePath": path,
"colorIdx": strToColor(fileName)
};
createProcess.command = ["touch", path];
createProcess.running = true; createProcess.running = true;
} }
function deleteNote(id) { function deleteNote(path) {
var filePath = notesDir + "/" + id + ".txt"; if (deleteProcess.running) {
var colorPath = notesDir + "/" + id + ".txt.color"; Logger.w("Notes", "Delete process is already running, skipping note deletion.");
deleteProcess.command = ["rm", "-f", filePath, colorPath]; return ;
}
deleteProcess.currentPath = path;
deleteProcess.command = ["rm", "-f", path];
deleteProcess.running = true; deleteProcess.running = true;
} }
function openNote(id) { function openNote(path) {
var filePath = notesDir + "/" + id + ".txt"; Quickshell.execDetached(["wezterm", "start", "--", "sh", "-c", `exec nvim "${path}"`]);
openProcess.command = ["gnome-text-editor", filePath];
openProcess.running = true;
} }
Component.onCompleted: { function strToColor(str) {
loadNotes(); let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) + hash) + str.charCodeAt(i);
}
return hash >>> 0;
} }
Process { function generateFileName() {
id: openProcess const timestamp = Time.timestamp;
const randomPart = Math.floor(Math.random() * 1e+06);
return `note_${timestamp}_${randomPart}.txt`;
} }
Process { Process {
id: createProcess id: createProcess
onExited: root.loadNotes() property var currentNote
onExited: (ret) => {
if (ret !== 0 || !currentNote) {
Logger.e("Notes", `Failed to create note file: ${ret}`);
return ;
}
notesModel.append(currentNote);
const toOpen = currentNote.notePath;
currentNote = null;
root.openNote(toOpen);
}
} }
Process { Process {
id: deleteProcess id: deleteProcess
onExited: root.loadNotes() property string currentPath
onExited: (ret) => {
if (ret !== 0) {
Logger.e("Notes", `Failed to delete note file: ${ret}`);
return ;
}
for (let i = 0; i < notesModel.count; i++) {
if (notesModel.get(i).notePath === currentPath) {
notesModel.remove(i);
break;
}
}
currentPath = "";
}
} }
Process { Process {
id: listProcess id: initProcess
command: ["sh", "-c", "mkdir -p " + notesDir + " && ls -1 " + notesDir + " | grep '\\.txt$' || true"] running: true
command: ["sh", "-c", "ls -p -tr " + Paths.notesDir]
stdout: StdioCollector { stdout: StdioCollector {
id: listCollector id: listCollector
onStreamFinished: { onStreamFinished: {
var files = listCollector.text.split('\n'); const files = listCollector.text.split('\n');
notesModel.clear(); notesModel.clear();
for (var i = 0; i < files.length; i++) { for (var i = 0; i < files.length; i++) {
if (files[i] === "") const fileName = files[i].trim();
if (!fileName || !fileName.endsWith(".txt"))
continue; continue;
var id = files[i].replace(".txt", "");
var contentFile = notesDir + "/" + files[i];
var colorFile = notesDir + "/" + files[i] + ".color";
// create an intermediate reader process
readProcessComponent.createObject(root, {
"noteId": id,
"contentFile": contentFile,
"colorFile": colorFile
}).run();
}
}
}
}
Component {
id: readProcessComponent
Process {
id: p
property string noteId
property string contentFile
property string colorFile
function run() {
running = true;
}
command: ["sh", "-c", "cat " + colorFile + " 2>/dev/null || echo 0; head -n 5 " + contentFile]
stdout: StdioCollector {
id: readCollector
onStreamFinished: {
var lines = readCollector.text.split('\n');
var colorIdx = parseInt(lines[0] || "0");
lines.shift();
var contentLines = lines.join('\n').trim();
notesModel.append({ notesModel.append({
"noteId": p.noteId, "notePath": Paths.notesDir + "/" + fileName,
"title": contentLines, "colorIdx": strToColor(fileName)
"colorIdx": colorIdx
}); });
p.destroy(); Logger.d("Notes", "Loaded note: " + fileName);
} }
} }
} }
} }