@@ -5,7 +5,7 @@ import Placeholder from "@tiptap/extension-placeholder";
55import TaskItem from "@tiptap/extension-task-item" ;
66import TaskList from "@tiptap/extension-task-list" ;
77import { Markdown , } from "@tiptap/markdown" ;
8- import { AllSelection , } from "@tiptap/pm/state" ;
8+ import { TextSelection , } from "@tiptap/pm/state" ;
99import type { EditorView , } from "@tiptap/pm/view" ;
1010import { EditorContent , useEditor , } from "@tiptap/react" ;
1111import StarterKit from "@tiptap/starter-kit" ;
@@ -29,6 +29,12 @@ import { LibraryDrawer, } from "../library/LibraryDrawer";
2929import { SettingsModal , } from "../settings/SettingsModal" ;
3030import { UpdateBanner , } from "../UpdateBanner" ;
3131
32+ function applyCity ( savedCity : string | null | undefined , newCity : string , ) : string {
33+ if ( ! savedCity || savedCity === newCity ) return newCity ;
34+ const from = savedCity . includes ( " → " , ) ? savedCity . split ( " → " , ) [ 0 ] : savedCity ;
35+ return `${ from } → ${ newCity } ` ;
36+ }
37+
3238function insertImageViaView ( file : File , view : EditorView , ) {
3339 saveImage ( file , ) . then ( async ( relativePath , ) => {
3440 const assetUrl = await resolveAssetUrl ( relativePath , ) ;
@@ -40,9 +46,8 @@ function insertImageViaView(file: File, view: EditorView,) {
4046 } , ) ;
4147}
4248
43- function DateHeader ( { date, } : { date : string ; } , ) {
49+ function DateHeader ( { date, city , } : { date : string ; city ?: string | null ; } , ) {
4450 const showToday = isToday ( date , ) ;
45- const city = useTimezoneCity ( ) ;
4651
4752 return (
4853 < div className = "flex items-center gap-4" >
@@ -60,7 +65,7 @@ function DateHeader({ date, }: { date: string; },) {
6065 today
6166 </ span >
6267 ) }
63- { showToday && city && (
68+ { city && (
6469 < span className = "text-sm text-gray-400 dark:text-gray-500 font-sans" >
6570 { city }
6671 </ span >
@@ -71,13 +76,20 @@ function DateHeader({ date, }: { date: string; },) {
7176
7277export default function AppLayout ( ) {
7378 const today = useCurrentDate ( ) ;
79+ const currentCity = useTimezoneCity ( ) ;
7480 const [ todayNote , setTodayNote , ] = useState < DailyNote | null > ( null , ) ;
7581 const [ pastNotes , setPastNotes , ] = useState < DailyNote [ ] > ( [ ] , ) ;
7682 const [ settingsOpen , setSettingsOpen , ] = useState ( false , ) ;
7783 const [ libraryOpen , setLibraryOpen , ] = useState ( false , ) ;
7884 const [ updateInfo , setUpdateInfo , ] = useState < UpdateInfo | null > ( null , ) ;
7985 const [ isPinned , setIsPinned , ] = useState ( false , ) ;
8086 const [ opacity , setOpacity , ] = useState ( 1 , ) ;
87+ const cityRef = useRef ( currentCity , ) ;
88+ const prevCityRef = useRef ( currentCity , ) ;
89+ const todayNoteRef = useRef < DailyNote | null > ( null , ) ;
90+ useEffect ( ( ) => {
91+ todayNoteRef . current = todayNote ;
92+ } , [ todayNote , ] , ) ;
8193
8294 // Extend FS scope for custom journal dir on mount
8395 useEffect ( ( ) => {
@@ -117,8 +129,17 @@ export default function AppLayout() {
117129 getOrCreateDailyNote ( today , ) ,
118130 loadPastNotes ( 30 , ) ,
119131 ] , ) ;
120- setTodayNote ( note , ) ;
132+
133+ // Apply current city: if the note's saved city differs, record the transition
134+ const effectiveCity = currentCity ? applyCity ( note . city , currentCity , ) : note . city ;
135+ const todayWithCity = { ...note , city : effectiveCity , } ;
136+ cityRef . current = effectiveCity ?? currentCity ?? "" ;
137+ setTodayNote ( todayWithCity , ) ;
121138 setPastNotes ( past , ) ;
139+ if ( effectiveCity !== note . city ) {
140+ selfWriteTimer . current = Date . now ( ) ;
141+ saveDailyNote ( todayWithCity , ) . catch ( console . error , ) ;
142+ }
122143
123144 // Build path → date map so the file watcher can identify changed notes
124145 const pastDates = Array . from ( { length : 30 , } , ( _ , i , ) => getDaysAgo ( i + 1 , ) , ) ;
@@ -134,6 +155,25 @@ export default function AppLayout() {
134155 load ( ) ;
135156 } , [ today , ] , ) ;
136157
158+ // Detect same-day timezone change (travel): update today's note with transition city.
159+ // Guard (note.date === today) prevents this from running when both today and currentCity
160+ // change simultaneously (cross-date-line travel) — the [today] load effect handles that.
161+ useEffect ( ( ) => {
162+ const prevCity = prevCityRef . current ;
163+ prevCityRef . current = currentCity ;
164+ if ( prevCity === currentCity ) return ;
165+
166+ const note = todayNoteRef . current ;
167+ if ( ! note || note . date !== today ) return ;
168+
169+ const transition = applyCity ( note . city ?? prevCity , currentCity , ) ;
170+ cityRef . current = transition ;
171+ const updated = { ...note , city : transition , } ;
172+ setTodayNote ( updated , ) ;
173+ selfWriteTimer . current = Date . now ( ) ;
174+ saveDailyNote ( updated , ) . catch ( console . error , ) ;
175+ } , [ currentCity , ] , ) ;
176+
137177 // Watch the journal directory for external changes
138178 useEffect ( ( ) => {
139179 let unwatch : ( ( ) => void ) | null = null ;
@@ -240,7 +280,9 @@ export default function AppLayout() {
240280 handleKeyDown : ( view , event , ) => {
241281 if ( ( event . metaKey || event . ctrlKey ) && event . key === "a" ) {
242282 event . preventDefault ( ) ;
243- view . dispatch ( view . state . tr . setSelection ( new AllSelection ( view . state . doc , ) , ) , ) ;
283+ view . dispatch (
284+ view . state . tr . setSelection ( TextSelection . create ( view . state . doc , 0 , view . state . doc . content . size , ) , ) ,
285+ ) ;
244286 return true ;
245287 }
246288 if ( ( event . metaKey || event . ctrlKey ) && event . key === "l" ) {
@@ -278,7 +320,7 @@ export default function AppLayout() {
278320 } ,
279321 onUpdate : ( { editor, } , ) => {
280322 if ( ! todayNote ) return ;
281- handleTodaySave ( { ...todayNote , content : editor . getMarkdown ( ) , } , ) ;
323+ handleTodaySave ( { ...todayNote , content : editor . getMarkdown ( ) , city : cityRef . current , } , ) ;
282324 } ,
283325 } , ) ;
284326
@@ -399,7 +441,7 @@ export default function AppLayout() {
399441 onClick = { ( ) => editor ?. commands . focus ( ) }
400442 >
401443 < div className = "px-6 pt-6 pb-4" >
402- < DateHeader date = { today } />
444+ < DateHeader date = { today } city = { currentCity } />
403445 </ div >
404446 { editor && < EditorBubbleMenu editor = { editor } /> }
405447 < EditorContent editor = { editor } />
@@ -411,7 +453,7 @@ export default function AppLayout() {
411453 < div className = "mx-6 border-t border-gray-200 dark:border-gray-700" />
412454 < div className = "min-h-[400px]" >
413455 < div className = "px-6 pt-12 pb-4" >
414- < DateHeader date = { note . date } />
456+ < DateHeader date = { note . date } city = { note . city } />
415457 </ div >
416458 < EditableNote note = { note } />
417459 </ div >
0 commit comments