Skip to content

Commit 7035174

Browse files
committed
configurable notes:path
1 parent 843f8c4 commit 7035174

9 files changed

Lines changed: 133 additions & 15 deletions

File tree

docs/docs/config.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ wsh editconfig
8484
| editor:inlinediff | bool | set to true to show diffs inline instead of side-by-side, false for side-by-side (defaults to undefined which uses Monaco's responsive behavior) |
8585
| preview:showhiddenfiles | bool | set to false to disable showing hidden files in the directory preview (defaults to true) |
8686
| preview:defaultsort <VersionBadge version="v0.14.2" /> | string | sets the default sort column for directory preview. `"name"` (default) sorts alphabetically by name ascending; `"modtime"` sorts by last modified time descending (newest first) |
87-
| notes:file <VersionBadge version="v0.14.6" /> | string | path to the notes markdown file (defaults to `~/notes.md`) |
87+
| notes:path <VersionBadge version="v0.14.6" /> | string | path to the notes markdown file (defaults to `~/notes.md`) |
8888
| markdown:fontsize | float64 | font size for the normal text when rendering markdown in preview. headers are scaled up from this size, (default 14px) |
8989
| markdown:fixedfontsize | float64 | font size for the code blocks when rendering markdown in preview (default is 12px) |
9090
| web:openlinksinternally | bool | set to false to open web links in external browser |

frontend/app/view/notes/notes-model.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,17 @@ export class NotesViewModel implements ViewModel {
178178
async loadFile() {
179179
try {
180180
const noteData = await this.env.rpc.GetNoteCommand(TabRpcClient);
181-
globalStore.set(this.contentAtom, noteData?.content ?? "");
182-
globalStore.set(this.filePathAtom, noteData?.filepath ?? "");
183-
globalStore.set(this.readOnlyAtom, noteData?.readonly ?? false);
184-
globalStore.set(this.loadErrorAtom, "");
181+
if (noteData?.error) {
182+
globalStore.set(this.loadErrorAtom, noteData.error);
183+
globalStore.set(this.contentAtom, "");
184+
globalStore.set(this.filePathAtom, "");
185+
globalStore.set(this.readOnlyAtom, false);
186+
} else {
187+
globalStore.set(this.contentAtom, noteData?.content ?? "");
188+
globalStore.set(this.filePathAtom, noteData?.filepath ?? "");
189+
globalStore.set(this.readOnlyAtom, noteData?.readonly ?? false);
190+
globalStore.set(this.loadErrorAtom, "");
191+
}
185192
} catch (err) {
186193
globalStore.set(this.loadErrorAtom, `Cannot load notes: ${err?.message ?? String(err)}`);
187194
} finally {
@@ -246,6 +253,26 @@ export class NotesViewModel implements ViewModel {
246253
}
247254
this.debouncedSave.cancel({ upcomingOnly: true });
248255
this.pendingContent = null;
256+
if (data?.error) {
257+
const editor = this.editorRef.current;
258+
if (editor != null) {
259+
const editorModel = editor.getModel();
260+
if (editorModel != null) {
261+
this.isApplyingRemoteEdit = true;
262+
try {
263+
editorModel.applyEdits([{ range: editorModel.getFullModelRange(), text: "" }]);
264+
} finally {
265+
this.isApplyingRemoteEdit = false;
266+
}
267+
}
268+
}
269+
globalStore.set(this.contentAtom, "");
270+
globalStore.set(this.filePathAtom, "");
271+
globalStore.set(this.readOnlyAtom, false);
272+
globalStore.set(this.loadErrorAtom, data.error);
273+
globalStore.set(this.saveStatusAtom, "idle");
274+
return;
275+
}
249276
const editor = this.editorRef.current;
250277
const content = data?.content ?? "";
251278
if (editor != null) {
@@ -260,6 +287,7 @@ export class NotesViewModel implements ViewModel {
260287
}
261288
}
262289
globalStore.set(this.contentAtom, content);
290+
globalStore.set(this.loadErrorAtom, "");
263291
if (data?.filepath) {
264292
globalStore.set(this.filePathAtom, data.filepath);
265293
}

frontend/types/gotypes.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1236,6 +1236,7 @@ declare global {
12361236
content: string;
12371237
readonly?: boolean;
12381238
filepath?: string;
1239+
error?: string;
12391240
};
12401241

12411242
// wshrpc.NotesUpdatedData
@@ -1244,6 +1245,7 @@ declare global {
12441245
sourceoref: string;
12451246
readonly?: boolean;
12461247
filepath?: string;
1248+
error?: string;
12471249
};
12481250

12491251
// waveobj.ORef
@@ -1506,6 +1508,7 @@ declare global {
15061508
"tsunami:sdkreplacepath"?: string;
15071509
"tsunami:sdkversion"?: string;
15081510
"tsunami:gopath"?: string;
1511+
"notes:path"?: string;
15091512
};
15101513

15111514
// waveobj.StickerClickOptsType

pkg/notesmanager/notesmanager.go

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package notesmanager
66
import (
77
"context"
88
"fmt"
9+
"log"
910
"os"
1011
"path/filepath"
1112
"strings"
@@ -24,11 +25,13 @@ type NotesManager struct {
2425
currentContent string
2526
lastWrittenModTime time.Time
2627
watcher *fsnotify.Watcher
28+
configuredPath string
2729
notesPath string
2830
tmpPath string
2931
initialized bool
3032
initErr error // fatal: reads and writes both fail
3133
readOnlyErr error // non-fatal: reads work, writes fail
34+
handlerRegistered bool
3235
}
3336

3437
var instance *NotesManager
@@ -43,18 +46,20 @@ func GetNotesManager() *NotesManager {
4346
return instance
4447
}
4548

46-
func notesFilePaths() (string, string, error) {
47-
configuredPath := wconfig.GetWatcher().GetFullConfig().Settings.NotesFile
49+
func notesFilePaths() (string, string, string, error) {
50+
configuredPath := wconfig.GetWatcher().GetFullConfig().Settings.NotesPath
4851
if configuredPath == "" {
4952
configuredPath = "~/notes.md"
5053
}
5154
notesPath, err := wavebase.ExpandHomeDir(configuredPath)
5255
if err != nil {
53-
return "", "", fmt.Errorf("expanding notes path: %w", err)
56+
return configuredPath, "", "", fmt.Errorf("expanding notes path: %w", err)
57+
}
58+
if !filepath.IsAbs(notesPath) {
59+
return configuredPath, "", "", fmt.Errorf("notes path %q must be an absolute path (fix via config key \"notes:path\")", configuredPath)
5460
}
55-
// derive tmp path: ~/notes.md -> ~/notes.md.tmp
5661
tmpPath := notesPath + ".tmp"
57-
return notesPath, tmpPath, nil
62+
return configuredPath, notesPath, tmpPath, nil
5863
}
5964

6065
func normalizeContent(s string) string {
@@ -69,14 +74,26 @@ func (nm *NotesManager) ensureInit() error {
6974
}
7075
nm.initialized = true
7176
nm.initErr = nm.doInit()
77+
if nm.initErr != nil {
78+
log.Printf("notesmanager: init error (configured path: %q): %v\n", nm.configuredPath, nm.initErr)
79+
} else {
80+
log.Printf("notesmanager: init success, path: %s\n", nm.notesPath)
81+
}
7282
return nm.initErr
7383
}
7484

7585
func (nm *NotesManager) doInit() error {
76-
notesPath, tmpPath, err := notesFilePaths()
86+
if !nm.handlerRegistered {
87+
wconfig.GetWatcher().RegisterUpdateHandler(nm.onConfigUpdate)
88+
nm.handlerRegistered = true
89+
}
90+
91+
configuredPath, notesPath, tmpPath, err := notesFilePaths()
7792
if err != nil {
93+
nm.configuredPath = configuredPath
7894
return err
7995
}
96+
nm.configuredPath = configuredPath
8097

8198
// reject if path is a directory or otherwise not a regular file
8299
if info, statErr := os.Stat(notesPath); statErr == nil {
@@ -97,7 +114,6 @@ func (nm *NotesManager) doInit() error {
97114

98115
nm.notesPath = notesPath
99116
nm.tmpPath = tmpPath
100-
fmt.Printf("notesmanager: notes path: %s\n", notesPath)
101117

102118
// probe directory write+delete permissions using the tmp path
103119
probeWriteErr := os.WriteFile(tmpPath, []byte{}, 0644)
@@ -197,6 +213,71 @@ func (nm *NotesManager) handleExternalChange() {
197213
})
198214
}
199215

216+
func (nm *NotesManager) resetInit() {
217+
if nm.watcher != nil {
218+
nm.watcher.Close()
219+
nm.watcher = nil
220+
}
221+
nm.initialized = false
222+
nm.initErr = nil
223+
nm.readOnlyErr = nil
224+
nm.configuredPath = ""
225+
nm.notesPath = ""
226+
nm.tmpPath = ""
227+
nm.lastWrittenModTime = time.Time{}
228+
nm.currentContent = ""
229+
}
230+
231+
func (nm *NotesManager) onConfigUpdate(_ wconfig.FullConfigType) {
232+
_, newNotesPath, _, pathErr := notesFilePaths()
233+
234+
nm.lock.Lock()
235+
if pathErr == nil && newNotesPath == nm.notesPath && nm.initErr == nil {
236+
nm.lock.Unlock()
237+
return
238+
}
239+
nm.resetInit()
240+
nm.lock.Unlock()
241+
242+
if pathErr != nil {
243+
wps.Broker.Publish(wps.WaveEvent{
244+
Event: wps.Event_NotesUpdated,
245+
Data: wshrpc.NotesUpdatedData{
246+
SourceOref: "pathchange",
247+
Error: pathErr.Error(),
248+
},
249+
})
250+
return
251+
}
252+
253+
nm.lock.Lock()
254+
initErr := nm.ensureInit()
255+
content := nm.currentContent
256+
notesPath := nm.notesPath
257+
readOnly := nm.readOnlyErr != nil
258+
nm.lock.Unlock()
259+
260+
if initErr != nil {
261+
wps.Broker.Publish(wps.WaveEvent{
262+
Event: wps.Event_NotesUpdated,
263+
Data: wshrpc.NotesUpdatedData{
264+
SourceOref: "pathchange",
265+
Error: initErr.Error(),
266+
},
267+
})
268+
return
269+
}
270+
wps.Broker.Publish(wps.WaveEvent{
271+
Event: wps.Event_NotesUpdated,
272+
Data: wshrpc.NotesUpdatedData{
273+
Content: content,
274+
SourceOref: "pathchange",
275+
ReadOnly: readOnly,
276+
FilePath: notesPath,
277+
},
278+
})
279+
}
280+
200281
func (nm *NotesManager) GetNote(ctx context.Context) (wshrpc.NoteData, error) {
201282
nm.lock.Lock()
202283
defer nm.lock.Unlock()

pkg/wconfig/defaultconfig/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,5 @@
4141
"waveai:showcloudmodes": true,
4242
"waveai:defaultmode": "waveai@balanced",
4343
"preview:defaultsort": "name",
44-
"notes:file": "~/notes.md"
44+
"notes:path": "~/notes.md"
4545
}

pkg/wconfig/metaconsts.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,7 @@ const (
131131
ConfigKey_TsunamiSdkReplacePath = "tsunami:sdkreplacepath"
132132
ConfigKey_TsunamiSdkVersion = "tsunami:sdkversion"
133133
ConfigKey_TsunamiGoPath = "tsunami:gopath"
134+
135+
ConfigKey_NotesPath = "notes:path"
134136
)
135137

pkg/wconfig/settingsconfig.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ type SettingsType struct {
183183
TsunamiSdkVersion string `json:"tsunami:sdkversion,omitempty"`
184184
TsunamiGoPath string `json:"tsunami:gopath,omitempty"`
185185

186-
NotesFile string `json:"notes:file,omitempty"`
186+
NotesPath string `json:"notes:path,omitempty"`
187187
}
188188

189189
func (s *SettingsType) GetAiSettings() *AiSettingsType {

pkg/wshrpc/wshrpctypes.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,6 @@ type CommandEventReadHistoryData struct {
343343
MaxItems int `json:"maxitems"`
344344
}
345345

346-
347346
type CpuDataRequest struct {
348347
Id string `json:"id"`
349348
Count int `json:"count"`
@@ -932,13 +931,15 @@ type NoteData struct {
932931
Content string `json:"content"`
933932
ReadOnly bool `json:"readonly,omitempty"`
934933
FilePath string `json:"filepath,omitempty"`
934+
Error string `json:"error,omitempty"`
935935
}
936936

937937
type NotesUpdatedData struct {
938938
Content string `json:"content"`
939939
SourceOref string `json:"sourceoref"`
940940
ReadOnly bool `json:"readonly,omitempty"`
941941
FilePath string `json:"filepath,omitempty"`
942+
Error string `json:"error,omitempty"`
942943
}
943944

944945
type CommandRemoteProcessSignalData struct {

schema/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,9 @@
351351
},
352352
"tsunami:gopath": {
353353
"type": "string"
354+
},
355+
"notes:path": {
356+
"type": "string"
354357
}
355358
},
356359
"additionalProperties": false,

0 commit comments

Comments
 (0)