[{"data":1,"prerenderedAt":4228},["ShallowReactive",2],{"blog-universal-media-seek-controls-with-hammerspoon":3,"blog-surround-universal-media-seek-controls-with-hammerspoon":1525,"blog-related-universal-media-seek-controls-with-hammerspoon":3765},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"topics":11,"body":15,"_type":1519,"_id":1520,"_source":1521,"_file":1522,"_stem":1523,"_extension":1524},"\u002Fblog\u002Funiversal-media-seek-controls-with-hammerspoon","blog",false,"","Universal media seek controls on macOS with Hammerspoon","A Hammerspoon script for universal media seeking across Spotify, Apple Music, and YouTube in Chrome.","2026-03-01T08:00:00+07:00",[12,13,14],"hammerspoon","macos","media",{"type":16,"children":17,"toc":1514},"root",[18,26,31,1036,1098,1105,1118,1202,1207,1213,1269,1275,1296,1508],{"type":19,"tag":20,"props":21,"children":22},"element","p",{},[23],{"type":24,"value":25},"text","I switch between Spotify, Apple Music, and YouTube in Chrome depending on what I'm doing. The problem is that each app has its own keyboard shortcuts for seeking, and none of them work when the app isn't focused. I wanted one hotkey to rewind 10 seconds and another to fast-forward 10 seconds, regardless of which player is running.",{"type":19,"tag":20,"props":27,"children":28},{},[29],{"type":24,"value":30},"Hammerspoon makes this pretty straightforward.",{"type":19,"tag":32,"props":33,"children":37},"pre",{"className":34,"code":35,"language":36,"meta":7,"style":7},"language-lua shiki shiki-themes one-dark-pro","local music_mod = {\"cmd\", \"alt\", \"ctrl\"}\n\n-- Seek the actively playing YouTube tab across all Chrome windows\nlocal function youtubeSeek(offset)\n    hs.osascript.applescript(string.format([[\n        tell application \"Google Chrome\"\n            repeat with w in windows\n                repeat with t in tabs of w\n                    if URL of t contains \"youtube.com\u002Fwatch\" then\n                        set isPaused to execute t javascript \"document.querySelector('video').paused\"\n                        if isPaused is \"false\" then\n                            set pos to execute t javascript \"document.querySelector('video').currentTime\"\n                            execute t javascript \"document.querySelector('video').currentTime = \" & (pos + %f)\n                            return\n                        end if\n                    end if\n                end repeat\n            end repeat\n        end tell\n    ]], offset))\nend\n\nlocal function mediaSeek(offset)\n    local spotify = hs.application.get(\"Spotify\")\n    if spotify and spotify:isRunning() and hs.spotify.getPlaybackState() == hs.spotify.state_playing then\n        hs.spotify.setPosition(hs.spotify.getPosition() + offset)\n        return\n    end\n    local music = hs.application.get(\"Music\")\n    if music and music:isRunning() and hs.itunes.getPlaybackState() == hs.itunes.state_playing then\n        hs.itunes.setPosition(hs.itunes.getPosition() + offset)\n        return\n    end\n    local chrome = hs.application.get(\"Google Chrome\")\n    if chrome and chrome:isRunning() then\n        youtubeSeek(offset)\n    end\nend\n\nhs.hotkey.bind(music_mod, \"left\", function()\n    mediaSeek(-10)\nend)\n\nhs.hotkey.bind(music_mod, \"right\", function()\n    mediaSeek(10)\nend)\n","lua",[38],{"type":19,"tag":39,"props":40,"children":41},"code",{"__ignoreMap":7},[42,96,106,116,151,189,198,207,216,225,234,243,252,261,270,279,288,297,306,315,337,346,354,383,430,507,555,564,573,615,681,725,733,741,783,819,840,848,856,864,914,938,951,959,1004,1024],{"type":19,"tag":43,"props":44,"children":47},"span",{"class":45,"line":46},"line",1,[48,54,60,66,72,77,82,86,91],{"type":19,"tag":43,"props":49,"children":51},{"style":50},"--shiki-default:#C678DD",[52],{"type":24,"value":53},"local",{"type":19,"tag":43,"props":55,"children":57},{"style":56},"--shiki-default:#E06C75",[58],{"type":24,"value":59}," music_mod",{"type":19,"tag":43,"props":61,"children":63},{"style":62},"--shiki-default:#ABB2BF",[64],{"type":24,"value":65}," = {",{"type":19,"tag":43,"props":67,"children":69},{"style":68},"--shiki-default:#98C379",[70],{"type":24,"value":71},"\"cmd\"",{"type":19,"tag":43,"props":73,"children":74},{"style":62},[75],{"type":24,"value":76},", ",{"type":19,"tag":43,"props":78,"children":79},{"style":68},[80],{"type":24,"value":81},"\"alt\"",{"type":19,"tag":43,"props":83,"children":84},{"style":62},[85],{"type":24,"value":76},{"type":19,"tag":43,"props":87,"children":88},{"style":68},[89],{"type":24,"value":90},"\"ctrl\"",{"type":19,"tag":43,"props":92,"children":93},{"style":62},[94],{"type":24,"value":95},"}\n",{"type":19,"tag":43,"props":97,"children":99},{"class":45,"line":98},2,[100],{"type":19,"tag":43,"props":101,"children":103},{"emptyLinePlaceholder":102},true,[104],{"type":24,"value":105},"\n",{"type":19,"tag":43,"props":107,"children":109},{"class":45,"line":108},3,[110],{"type":19,"tag":43,"props":111,"children":113},{"style":112},"--shiki-default:#7F848E;--shiki-default-font-style:italic",[114],{"type":24,"value":115},"-- Seek the actively playing YouTube tab across all Chrome windows\n",{"type":19,"tag":43,"props":117,"children":119},{"class":45,"line":118},4,[120,124,129,135,140,146],{"type":19,"tag":43,"props":121,"children":122},{"style":50},[123],{"type":24,"value":53},{"type":19,"tag":43,"props":125,"children":126},{"style":50},[127],{"type":24,"value":128}," function",{"type":19,"tag":43,"props":130,"children":132},{"style":131},"--shiki-default:#61AFEF",[133],{"type":24,"value":134}," youtubeSeek",{"type":19,"tag":43,"props":136,"children":137},{"style":62},[138],{"type":24,"value":139},"(",{"type":19,"tag":43,"props":141,"children":143},{"style":142},"--shiki-default:#ABB2BF;--shiki-default-font-style:italic",[144],{"type":24,"value":145},"offset",{"type":19,"tag":43,"props":147,"children":148},{"style":62},[149],{"type":24,"value":150},")\n",{"type":19,"tag":43,"props":152,"children":154},{"class":45,"line":153},5,[155,160,165,170,174,180,184],{"type":19,"tag":43,"props":156,"children":157},{"style":56},[158],{"type":24,"value":159},"    hs",{"type":19,"tag":43,"props":161,"children":162},{"style":62},[163],{"type":24,"value":164},".osascript.",{"type":19,"tag":43,"props":166,"children":167},{"style":131},[168],{"type":24,"value":169},"applescript",{"type":19,"tag":43,"props":171,"children":172},{"style":62},[173],{"type":24,"value":139},{"type":19,"tag":43,"props":175,"children":177},{"style":176},"--shiki-default:#56B6C2",[178],{"type":24,"value":179},"string.format",{"type":19,"tag":43,"props":181,"children":182},{"style":62},[183],{"type":24,"value":139},{"type":19,"tag":43,"props":185,"children":186},{"style":68},[187],{"type":24,"value":188},"[[\n",{"type":19,"tag":43,"props":190,"children":192},{"class":45,"line":191},6,[193],{"type":19,"tag":43,"props":194,"children":195},{"style":68},[196],{"type":24,"value":197},"        tell application \"Google Chrome\"\n",{"type":19,"tag":43,"props":199,"children":201},{"class":45,"line":200},7,[202],{"type":19,"tag":43,"props":203,"children":204},{"style":68},[205],{"type":24,"value":206},"            repeat with w in windows\n",{"type":19,"tag":43,"props":208,"children":210},{"class":45,"line":209},8,[211],{"type":19,"tag":43,"props":212,"children":213},{"style":68},[214],{"type":24,"value":215},"                repeat with t in tabs of w\n",{"type":19,"tag":43,"props":217,"children":219},{"class":45,"line":218},9,[220],{"type":19,"tag":43,"props":221,"children":222},{"style":68},[223],{"type":24,"value":224},"                    if URL of t contains \"youtube.com\u002Fwatch\" then\n",{"type":19,"tag":43,"props":226,"children":228},{"class":45,"line":227},10,[229],{"type":19,"tag":43,"props":230,"children":231},{"style":68},[232],{"type":24,"value":233},"                        set isPaused to execute t javascript \"document.querySelector('video').paused\"\n",{"type":19,"tag":43,"props":235,"children":237},{"class":45,"line":236},11,[238],{"type":19,"tag":43,"props":239,"children":240},{"style":68},[241],{"type":24,"value":242},"                        if isPaused is \"false\" then\n",{"type":19,"tag":43,"props":244,"children":246},{"class":45,"line":245},12,[247],{"type":19,"tag":43,"props":248,"children":249},{"style":68},[250],{"type":24,"value":251},"                            set pos to execute t javascript \"document.querySelector('video').currentTime\"\n",{"type":19,"tag":43,"props":253,"children":255},{"class":45,"line":254},13,[256],{"type":19,"tag":43,"props":257,"children":258},{"style":68},[259],{"type":24,"value":260},"                            execute t javascript \"document.querySelector('video').currentTime = \" & (pos + %f)\n",{"type":19,"tag":43,"props":262,"children":264},{"class":45,"line":263},14,[265],{"type":19,"tag":43,"props":266,"children":267},{"style":68},[268],{"type":24,"value":269},"                            return\n",{"type":19,"tag":43,"props":271,"children":273},{"class":45,"line":272},15,[274],{"type":19,"tag":43,"props":275,"children":276},{"style":68},[277],{"type":24,"value":278},"                        end if\n",{"type":19,"tag":43,"props":280,"children":282},{"class":45,"line":281},16,[283],{"type":19,"tag":43,"props":284,"children":285},{"style":68},[286],{"type":24,"value":287},"                    end if\n",{"type":19,"tag":43,"props":289,"children":291},{"class":45,"line":290},17,[292],{"type":19,"tag":43,"props":293,"children":294},{"style":68},[295],{"type":24,"value":296},"                end repeat\n",{"type":19,"tag":43,"props":298,"children":300},{"class":45,"line":299},18,[301],{"type":19,"tag":43,"props":302,"children":303},{"style":68},[304],{"type":24,"value":305},"            end repeat\n",{"type":19,"tag":43,"props":307,"children":309},{"class":45,"line":308},19,[310],{"type":19,"tag":43,"props":311,"children":312},{"style":68},[313],{"type":24,"value":314},"        end tell\n",{"type":19,"tag":43,"props":316,"children":318},{"class":45,"line":317},20,[319,324,328,332],{"type":19,"tag":43,"props":320,"children":321},{"style":68},[322],{"type":24,"value":323},"    ]]",{"type":19,"tag":43,"props":325,"children":326},{"style":62},[327],{"type":24,"value":76},{"type":19,"tag":43,"props":329,"children":330},{"style":56},[331],{"type":24,"value":145},{"type":19,"tag":43,"props":333,"children":334},{"style":62},[335],{"type":24,"value":336},"))\n",{"type":19,"tag":43,"props":338,"children":340},{"class":45,"line":339},21,[341],{"type":19,"tag":43,"props":342,"children":343},{"style":50},[344],{"type":24,"value":345},"end\n",{"type":19,"tag":43,"props":347,"children":349},{"class":45,"line":348},22,[350],{"type":19,"tag":43,"props":351,"children":352},{"emptyLinePlaceholder":102},[353],{"type":24,"value":105},{"type":19,"tag":43,"props":355,"children":357},{"class":45,"line":356},23,[358,362,366,371,375,379],{"type":19,"tag":43,"props":359,"children":360},{"style":50},[361],{"type":24,"value":53},{"type":19,"tag":43,"props":363,"children":364},{"style":50},[365],{"type":24,"value":128},{"type":19,"tag":43,"props":367,"children":368},{"style":131},[369],{"type":24,"value":370}," mediaSeek",{"type":19,"tag":43,"props":372,"children":373},{"style":62},[374],{"type":24,"value":139},{"type":19,"tag":43,"props":376,"children":377},{"style":142},[378],{"type":24,"value":145},{"type":19,"tag":43,"props":380,"children":381},{"style":62},[382],{"type":24,"value":150},{"type":19,"tag":43,"props":384,"children":386},{"class":45,"line":385},24,[387,392,397,402,407,412,417,421,426],{"type":19,"tag":43,"props":388,"children":389},{"style":50},[390],{"type":24,"value":391},"    local",{"type":19,"tag":43,"props":393,"children":394},{"style":56},[395],{"type":24,"value":396}," spotify",{"type":19,"tag":43,"props":398,"children":399},{"style":62},[400],{"type":24,"value":401}," = ",{"type":19,"tag":43,"props":403,"children":404},{"style":56},[405],{"type":24,"value":406},"hs",{"type":19,"tag":43,"props":408,"children":409},{"style":62},[410],{"type":24,"value":411},".application.",{"type":19,"tag":43,"props":413,"children":414},{"style":131},[415],{"type":24,"value":416},"get",{"type":19,"tag":43,"props":418,"children":419},{"style":62},[420],{"type":24,"value":139},{"type":19,"tag":43,"props":422,"children":423},{"style":68},[424],{"type":24,"value":425},"\"Spotify\"",{"type":19,"tag":43,"props":427,"children":428},{"style":62},[429],{"type":24,"value":150},{"type":19,"tag":43,"props":431,"children":433},{"class":45,"line":432},25,[434,439,443,448,453,458,463,468,473,478,483,488,493,497,502],{"type":19,"tag":43,"props":435,"children":436},{"style":50},[437],{"type":24,"value":438},"    if",{"type":19,"tag":43,"props":440,"children":441},{"style":56},[442],{"type":24,"value":396},{"type":19,"tag":43,"props":444,"children":445},{"style":176},[446],{"type":24,"value":447}," and",{"type":19,"tag":43,"props":449,"children":451},{"style":450},"--shiki-default:#E5C07B",[452],{"type":24,"value":396},{"type":19,"tag":43,"props":454,"children":455},{"style":62},[456],{"type":24,"value":457},":",{"type":19,"tag":43,"props":459,"children":460},{"style":131},[461],{"type":24,"value":462},"isRunning",{"type":19,"tag":43,"props":464,"children":465},{"style":62},[466],{"type":24,"value":467},"() ",{"type":19,"tag":43,"props":469,"children":470},{"style":176},[471],{"type":24,"value":472},"and",{"type":19,"tag":43,"props":474,"children":475},{"style":56},[476],{"type":24,"value":477}," hs",{"type":19,"tag":43,"props":479,"children":480},{"style":62},[481],{"type":24,"value":482},".spotify.",{"type":19,"tag":43,"props":484,"children":485},{"style":131},[486],{"type":24,"value":487},"getPlaybackState",{"type":19,"tag":43,"props":489,"children":490},{"style":62},[491],{"type":24,"value":492},"() == ",{"type":19,"tag":43,"props":494,"children":495},{"style":56},[496],{"type":24,"value":406},{"type":19,"tag":43,"props":498,"children":499},{"style":62},[500],{"type":24,"value":501},".spotify.state_playing ",{"type":19,"tag":43,"props":503,"children":504},{"style":50},[505],{"type":24,"value":506},"then\n",{"type":19,"tag":43,"props":508,"children":510},{"class":45,"line":509},26,[511,516,520,525,529,533,537,542,547,551],{"type":19,"tag":43,"props":512,"children":513},{"style":56},[514],{"type":24,"value":515},"        hs",{"type":19,"tag":43,"props":517,"children":518},{"style":62},[519],{"type":24,"value":482},{"type":19,"tag":43,"props":521,"children":522},{"style":131},[523],{"type":24,"value":524},"setPosition",{"type":19,"tag":43,"props":526,"children":527},{"style":62},[528],{"type":24,"value":139},{"type":19,"tag":43,"props":530,"children":531},{"style":56},[532],{"type":24,"value":406},{"type":19,"tag":43,"props":534,"children":535},{"style":62},[536],{"type":24,"value":482},{"type":19,"tag":43,"props":538,"children":539},{"style":131},[540],{"type":24,"value":541},"getPosition",{"type":19,"tag":43,"props":543,"children":544},{"style":62},[545],{"type":24,"value":546},"() + ",{"type":19,"tag":43,"props":548,"children":549},{"style":56},[550],{"type":24,"value":145},{"type":19,"tag":43,"props":552,"children":553},{"style":62},[554],{"type":24,"value":150},{"type":19,"tag":43,"props":556,"children":558},{"class":45,"line":557},27,[559],{"type":19,"tag":43,"props":560,"children":561},{"style":50},[562],{"type":24,"value":563},"        return\n",{"type":19,"tag":43,"props":565,"children":567},{"class":45,"line":566},28,[568],{"type":19,"tag":43,"props":569,"children":570},{"style":50},[571],{"type":24,"value":572},"    end\n",{"type":19,"tag":43,"props":574,"children":576},{"class":45,"line":575},29,[577,581,586,590,594,598,602,606,611],{"type":19,"tag":43,"props":578,"children":579},{"style":50},[580],{"type":24,"value":391},{"type":19,"tag":43,"props":582,"children":583},{"style":56},[584],{"type":24,"value":585}," music",{"type":19,"tag":43,"props":587,"children":588},{"style":62},[589],{"type":24,"value":401},{"type":19,"tag":43,"props":591,"children":592},{"style":56},[593],{"type":24,"value":406},{"type":19,"tag":43,"props":595,"children":596},{"style":62},[597],{"type":24,"value":411},{"type":19,"tag":43,"props":599,"children":600},{"style":131},[601],{"type":24,"value":416},{"type":19,"tag":43,"props":603,"children":604},{"style":62},[605],{"type":24,"value":139},{"type":19,"tag":43,"props":607,"children":608},{"style":68},[609],{"type":24,"value":610},"\"Music\"",{"type":19,"tag":43,"props":612,"children":613},{"style":62},[614],{"type":24,"value":150},{"type":19,"tag":43,"props":616,"children":618},{"class":45,"line":617},30,[619,623,627,631,635,639,643,647,651,655,660,664,668,672,677],{"type":19,"tag":43,"props":620,"children":621},{"style":50},[622],{"type":24,"value":438},{"type":19,"tag":43,"props":624,"children":625},{"style":56},[626],{"type":24,"value":585},{"type":19,"tag":43,"props":628,"children":629},{"style":176},[630],{"type":24,"value":447},{"type":19,"tag":43,"props":632,"children":633},{"style":450},[634],{"type":24,"value":585},{"type":19,"tag":43,"props":636,"children":637},{"style":62},[638],{"type":24,"value":457},{"type":19,"tag":43,"props":640,"children":641},{"style":131},[642],{"type":24,"value":462},{"type":19,"tag":43,"props":644,"children":645},{"style":62},[646],{"type":24,"value":467},{"type":19,"tag":43,"props":648,"children":649},{"style":176},[650],{"type":24,"value":472},{"type":19,"tag":43,"props":652,"children":653},{"style":56},[654],{"type":24,"value":477},{"type":19,"tag":43,"props":656,"children":657},{"style":62},[658],{"type":24,"value":659},".itunes.",{"type":19,"tag":43,"props":661,"children":662},{"style":131},[663],{"type":24,"value":487},{"type":19,"tag":43,"props":665,"children":666},{"style":62},[667],{"type":24,"value":492},{"type":19,"tag":43,"props":669,"children":670},{"style":56},[671],{"type":24,"value":406},{"type":19,"tag":43,"props":673,"children":674},{"style":62},[675],{"type":24,"value":676},".itunes.state_playing ",{"type":19,"tag":43,"props":678,"children":679},{"style":50},[680],{"type":24,"value":506},{"type":19,"tag":43,"props":682,"children":684},{"class":45,"line":683},31,[685,689,693,697,701,705,709,713,717,721],{"type":19,"tag":43,"props":686,"children":687},{"style":56},[688],{"type":24,"value":515},{"type":19,"tag":43,"props":690,"children":691},{"style":62},[692],{"type":24,"value":659},{"type":19,"tag":43,"props":694,"children":695},{"style":131},[696],{"type":24,"value":524},{"type":19,"tag":43,"props":698,"children":699},{"style":62},[700],{"type":24,"value":139},{"type":19,"tag":43,"props":702,"children":703},{"style":56},[704],{"type":24,"value":406},{"type":19,"tag":43,"props":706,"children":707},{"style":62},[708],{"type":24,"value":659},{"type":19,"tag":43,"props":710,"children":711},{"style":131},[712],{"type":24,"value":541},{"type":19,"tag":43,"props":714,"children":715},{"style":62},[716],{"type":24,"value":546},{"type":19,"tag":43,"props":718,"children":719},{"style":56},[720],{"type":24,"value":145},{"type":19,"tag":43,"props":722,"children":723},{"style":62},[724],{"type":24,"value":150},{"type":19,"tag":43,"props":726,"children":728},{"class":45,"line":727},32,[729],{"type":19,"tag":43,"props":730,"children":731},{"style":50},[732],{"type":24,"value":563},{"type":19,"tag":43,"props":734,"children":736},{"class":45,"line":735},33,[737],{"type":19,"tag":43,"props":738,"children":739},{"style":50},[740],{"type":24,"value":572},{"type":19,"tag":43,"props":742,"children":744},{"class":45,"line":743},34,[745,749,754,758,762,766,770,774,779],{"type":19,"tag":43,"props":746,"children":747},{"style":50},[748],{"type":24,"value":391},{"type":19,"tag":43,"props":750,"children":751},{"style":56},[752],{"type":24,"value":753}," chrome",{"type":19,"tag":43,"props":755,"children":756},{"style":62},[757],{"type":24,"value":401},{"type":19,"tag":43,"props":759,"children":760},{"style":56},[761],{"type":24,"value":406},{"type":19,"tag":43,"props":763,"children":764},{"style":62},[765],{"type":24,"value":411},{"type":19,"tag":43,"props":767,"children":768},{"style":131},[769],{"type":24,"value":416},{"type":19,"tag":43,"props":771,"children":772},{"style":62},[773],{"type":24,"value":139},{"type":19,"tag":43,"props":775,"children":776},{"style":68},[777],{"type":24,"value":778},"\"Google Chrome\"",{"type":19,"tag":43,"props":780,"children":781},{"style":62},[782],{"type":24,"value":150},{"type":19,"tag":43,"props":784,"children":786},{"class":45,"line":785},35,[787,791,795,799,803,807,811,815],{"type":19,"tag":43,"props":788,"children":789},{"style":50},[790],{"type":24,"value":438},{"type":19,"tag":43,"props":792,"children":793},{"style":56},[794],{"type":24,"value":753},{"type":19,"tag":43,"props":796,"children":797},{"style":176},[798],{"type":24,"value":447},{"type":19,"tag":43,"props":800,"children":801},{"style":450},[802],{"type":24,"value":753},{"type":19,"tag":43,"props":804,"children":805},{"style":62},[806],{"type":24,"value":457},{"type":19,"tag":43,"props":808,"children":809},{"style":131},[810],{"type":24,"value":462},{"type":19,"tag":43,"props":812,"children":813},{"style":62},[814],{"type":24,"value":467},{"type":19,"tag":43,"props":816,"children":817},{"style":50},[818],{"type":24,"value":506},{"type":19,"tag":43,"props":820,"children":822},{"class":45,"line":821},36,[823,828,832,836],{"type":19,"tag":43,"props":824,"children":825},{"style":131},[826],{"type":24,"value":827},"        youtubeSeek",{"type":19,"tag":43,"props":829,"children":830},{"style":62},[831],{"type":24,"value":139},{"type":19,"tag":43,"props":833,"children":834},{"style":56},[835],{"type":24,"value":145},{"type":19,"tag":43,"props":837,"children":838},{"style":62},[839],{"type":24,"value":150},{"type":19,"tag":43,"props":841,"children":843},{"class":45,"line":842},37,[844],{"type":19,"tag":43,"props":845,"children":846},{"style":50},[847],{"type":24,"value":572},{"type":19,"tag":43,"props":849,"children":851},{"class":45,"line":850},38,[852],{"type":19,"tag":43,"props":853,"children":854},{"style":50},[855],{"type":24,"value":345},{"type":19,"tag":43,"props":857,"children":859},{"class":45,"line":858},39,[860],{"type":19,"tag":43,"props":861,"children":862},{"emptyLinePlaceholder":102},[863],{"type":24,"value":105},{"type":19,"tag":43,"props":865,"children":867},{"class":45,"line":866},40,[868,872,877,882,886,891,895,900,904,909],{"type":19,"tag":43,"props":869,"children":870},{"style":56},[871],{"type":24,"value":406},{"type":19,"tag":43,"props":873,"children":874},{"style":62},[875],{"type":24,"value":876},".hotkey.",{"type":19,"tag":43,"props":878,"children":879},{"style":131},[880],{"type":24,"value":881},"bind",{"type":19,"tag":43,"props":883,"children":884},{"style":62},[885],{"type":24,"value":139},{"type":19,"tag":43,"props":887,"children":888},{"style":56},[889],{"type":24,"value":890},"music_mod",{"type":19,"tag":43,"props":892,"children":893},{"style":62},[894],{"type":24,"value":76},{"type":19,"tag":43,"props":896,"children":897},{"style":68},[898],{"type":24,"value":899},"\"left\"",{"type":19,"tag":43,"props":901,"children":902},{"style":62},[903],{"type":24,"value":76},{"type":19,"tag":43,"props":905,"children":906},{"style":50},[907],{"type":24,"value":908},"function",{"type":19,"tag":43,"props":910,"children":911},{"style":62},[912],{"type":24,"value":913},"()\n",{"type":19,"tag":43,"props":915,"children":917},{"class":45,"line":916},41,[918,923,928,934],{"type":19,"tag":43,"props":919,"children":920},{"style":131},[921],{"type":24,"value":922},"    mediaSeek",{"type":19,"tag":43,"props":924,"children":925},{"style":62},[926],{"type":24,"value":927},"(-",{"type":19,"tag":43,"props":929,"children":931},{"style":930},"--shiki-default:#D19A66",[932],{"type":24,"value":933},"10",{"type":19,"tag":43,"props":935,"children":936},{"style":62},[937],{"type":24,"value":150},{"type":19,"tag":43,"props":939,"children":941},{"class":45,"line":940},42,[942,947],{"type":19,"tag":43,"props":943,"children":944},{"style":50},[945],{"type":24,"value":946},"end",{"type":19,"tag":43,"props":948,"children":949},{"style":62},[950],{"type":24,"value":150},{"type":19,"tag":43,"props":952,"children":954},{"class":45,"line":953},43,[955],{"type":19,"tag":43,"props":956,"children":957},{"emptyLinePlaceholder":102},[958],{"type":24,"value":105},{"type":19,"tag":43,"props":960,"children":962},{"class":45,"line":961},44,[963,967,971,975,979,983,987,992,996,1000],{"type":19,"tag":43,"props":964,"children":965},{"style":56},[966],{"type":24,"value":406},{"type":19,"tag":43,"props":968,"children":969},{"style":62},[970],{"type":24,"value":876},{"type":19,"tag":43,"props":972,"children":973},{"style":131},[974],{"type":24,"value":881},{"type":19,"tag":43,"props":976,"children":977},{"style":62},[978],{"type":24,"value":139},{"type":19,"tag":43,"props":980,"children":981},{"style":56},[982],{"type":24,"value":890},{"type":19,"tag":43,"props":984,"children":985},{"style":62},[986],{"type":24,"value":76},{"type":19,"tag":43,"props":988,"children":989},{"style":68},[990],{"type":24,"value":991},"\"right\"",{"type":19,"tag":43,"props":993,"children":994},{"style":62},[995],{"type":24,"value":76},{"type":19,"tag":43,"props":997,"children":998},{"style":50},[999],{"type":24,"value":908},{"type":19,"tag":43,"props":1001,"children":1002},{"style":62},[1003],{"type":24,"value":913},{"type":19,"tag":43,"props":1005,"children":1007},{"class":45,"line":1006},45,[1008,1012,1016,1020],{"type":19,"tag":43,"props":1009,"children":1010},{"style":131},[1011],{"type":24,"value":922},{"type":19,"tag":43,"props":1013,"children":1014},{"style":62},[1015],{"type":24,"value":139},{"type":19,"tag":43,"props":1017,"children":1018},{"style":930},[1019],{"type":24,"value":933},{"type":19,"tag":43,"props":1021,"children":1022},{"style":62},[1023],{"type":24,"value":150},{"type":19,"tag":43,"props":1025,"children":1027},{"class":45,"line":1026},46,[1028,1032],{"type":19,"tag":43,"props":1029,"children":1030},{"style":50},[1031],{"type":24,"value":946},{"type":19,"tag":43,"props":1033,"children":1034},{"style":62},[1035],{"type":24,"value":150},{"type":19,"tag":1037,"props":1038,"children":1039},"table",{},[1040,1059],{"type":19,"tag":1041,"props":1042,"children":1043},"thead",{},[1044],{"type":19,"tag":1045,"props":1046,"children":1047},"tr",{},[1048,1054],{"type":19,"tag":1049,"props":1050,"children":1051},"th",{},[1052],{"type":24,"value":1053},"Shortcut",{"type":19,"tag":1049,"props":1055,"children":1056},{},[1057],{"type":24,"value":1058},"Action",{"type":19,"tag":1060,"props":1061,"children":1062},"tbody",{},[1063,1081],{"type":19,"tag":1045,"props":1064,"children":1065},{},[1066,1076],{"type":19,"tag":1067,"props":1068,"children":1069},"td",{},[1070],{"type":19,"tag":39,"props":1071,"children":1073},{"className":1072},[],[1074],{"type":24,"value":1075},"Cmd + Alt + Ctrl + Left",{"type":19,"tag":1067,"props":1077,"children":1078},{},[1079],{"type":24,"value":1080},"Rewind 10 seconds",{"type":19,"tag":1045,"props":1082,"children":1083},{},[1084,1093],{"type":19,"tag":1067,"props":1085,"children":1086},{},[1087],{"type":19,"tag":39,"props":1088,"children":1090},{"className":1089},[],[1091],{"type":24,"value":1092},"Cmd + Alt + Ctrl + Right",{"type":19,"tag":1067,"props":1094,"children":1095},{},[1096],{"type":24,"value":1097},"Fast-forward 10 seconds",{"type":19,"tag":1099,"props":1100,"children":1102},"h2",{"id":1101},"how-it-picks-the-player",[1103],{"type":24,"value":1104},"How it picks the player",{"type":19,"tag":20,"props":1106,"children":1107},{},[1108,1110,1116],{"type":24,"value":1109},"The ",{"type":19,"tag":39,"props":1111,"children":1113},{"className":1112},[],[1114],{"type":24,"value":1115},"mediaSeek",{"type":24,"value":1117}," function checks which app is actively playing, not just which app is running:",{"type":19,"tag":1119,"props":1120,"children":1121},"ol",{},[1122,1150,1168],{"type":19,"tag":1123,"props":1124,"children":1125},"li",{},[1126,1132,1134,1140,1142,1148],{"type":19,"tag":1127,"props":1128,"children":1129},"strong",{},[1130],{"type":24,"value":1131},"Spotify",{"type":24,"value":1133}," - checks ",{"type":19,"tag":39,"props":1135,"children":1137},{"className":1136},[],[1138],{"type":24,"value":1139},"getPlaybackState()",{"type":24,"value":1141}," against ",{"type":19,"tag":39,"props":1143,"children":1145},{"className":1144},[],[1146],{"type":24,"value":1147},"state_playing",{"type":24,"value":1149}," to confirm Spotify is actively playing, not just open.",{"type":19,"tag":1123,"props":1151,"children":1152},{},[1153,1158,1160,1166],{"type":19,"tag":1127,"props":1154,"children":1155},{},[1156],{"type":24,"value":1157},"Apple Music",{"type":24,"value":1159}," - same check via ",{"type":19,"tag":39,"props":1161,"children":1163},{"className":1162},[],[1164],{"type":24,"value":1165},"hs.itunes",{"type":24,"value":1167}," (Hammerspoon still uses the old iTunes module name).",{"type":19,"tag":1123,"props":1169,"children":1170},{},[1171,1176,1178,1184,1186,1192,1194,1200],{"type":19,"tag":1127,"props":1172,"children":1173},{},[1174],{"type":24,"value":1175},"YouTube in Chrome",{"type":24,"value":1177}," - if neither desktop player is active, it scans all Chrome windows and tabs for a ",{"type":19,"tag":39,"props":1179,"children":1181},{"className":1180},[],[1182],{"type":24,"value":1183},"youtube.com\u002Fwatch",{"type":24,"value":1185}," page where ",{"type":19,"tag":39,"props":1187,"children":1189},{"className":1188},[],[1190],{"type":24,"value":1191},"video.paused",{"type":24,"value":1193}," is ",{"type":19,"tag":39,"props":1195,"children":1197},{"className":1196},[],[1198],{"type":24,"value":1199},"false",{"type":24,"value":1201},". If you have multiple YouTube tabs open, only the one that's actually playing gets seeked.",{"type":19,"tag":20,"props":1203,"children":1204},{},[1205],{"type":24,"value":1206},"First active player wins. If nothing is playing, the hotkey does nothing.",{"type":19,"tag":1099,"props":1208,"children":1210},{"id":1209},"setup",[1211],{"type":24,"value":1212},"Setup",{"type":19,"tag":1119,"props":1214,"children":1215},{},[1216,1232,1244,1256],{"type":19,"tag":1123,"props":1217,"children":1218},{},[1219,1221,1230],{"type":24,"value":1220},"Install ",{"type":19,"tag":1222,"props":1223,"children":1227},"a",{"href":1224,"rel":1225},"https:\u002F\u002Fwww.hammerspoon.org\u002F",[1226],"nofollow",[1228],{"type":24,"value":1229},"Hammerspoon",{"type":24,"value":1231}," if you haven't already.",{"type":19,"tag":1123,"props":1233,"children":1234},{},[1235,1237,1242],{"type":24,"value":1236},"For YouTube control, enable JavaScript execution in Chrome: ",{"type":19,"tag":1127,"props":1238,"children":1239},{},[1240],{"type":24,"value":1241},"View > Developer > Allow JavaScript from Apple Events",{"type":24,"value":1243},".",{"type":19,"tag":1123,"props":1245,"children":1246},{},[1247,1249,1255],{"type":24,"value":1248},"Add the script to your ",{"type":19,"tag":39,"props":1250,"children":1252},{"className":1251},[],[1253],{"type":24,"value":1254},"~\u002F.hammerspoon\u002Finit.lua",{"type":24,"value":1243},{"type":19,"tag":1123,"props":1257,"children":1258},{},[1259,1261,1267],{"type":24,"value":1260},"Reload the config with ",{"type":19,"tag":39,"props":1262,"children":1264},{"className":1263},[],[1265],{"type":24,"value":1266},"Cmd + Alt + Ctrl + R",{"type":24,"value":1268}," (or click \"Reload Config\" from the Hammerspoon menu bar icon).",{"type":19,"tag":1099,"props":1270,"children":1272},{"id":1271},"tweaking",[1273],{"type":24,"value":1274},"Tweaking",{"type":19,"tag":20,"props":1276,"children":1277},{},[1278,1280,1286,1288,1294],{"type":24,"value":1279},"The offset is just a number in seconds, so you can change ",{"type":19,"tag":39,"props":1281,"children":1283},{"className":1282},[],[1284],{"type":24,"value":1285},"mediaSeek(-10)",{"type":24,"value":1287}," and ",{"type":19,"tag":39,"props":1289,"children":1291},{"className":1290},[],[1292],{"type":24,"value":1293},"mediaSeek(10)",{"type":24,"value":1295}," to whatever you want. You could also add separate bindings for smaller jumps:",{"type":19,"tag":32,"props":1297,"children":1299},{"className":34,"code":1298,"language":36,"meta":7,"style":7},"hs.hotkey.bind({\"cmd\", \"alt\", \"ctrl\", \"shift\"}, \"left\", function()\n    mediaSeek(-5)\nend)\n\nhs.hotkey.bind({\"cmd\", \"alt\", \"ctrl\", \"shift\"}, \"right\", function()\n    mediaSeek(5)\nend)\n",[1300],{"type":19,"tag":39,"props":1301,"children":1302},{"__ignoreMap":7},[1303,1373,1393,1404,1411,1478,1497],{"type":19,"tag":43,"props":1304,"children":1305},{"class":45,"line":46},[1306,1310,1314,1318,1323,1327,1331,1335,1339,1343,1347,1352,1357,1361,1365,1369],{"type":19,"tag":43,"props":1307,"children":1308},{"style":56},[1309],{"type":24,"value":406},{"type":19,"tag":43,"props":1311,"children":1312},{"style":62},[1313],{"type":24,"value":876},{"type":19,"tag":43,"props":1315,"children":1316},{"style":131},[1317],{"type":24,"value":881},{"type":19,"tag":43,"props":1319,"children":1320},{"style":62},[1321],{"type":24,"value":1322},"({",{"type":19,"tag":43,"props":1324,"children":1325},{"style":68},[1326],{"type":24,"value":71},{"type":19,"tag":43,"props":1328,"children":1329},{"style":62},[1330],{"type":24,"value":76},{"type":19,"tag":43,"props":1332,"children":1333},{"style":68},[1334],{"type":24,"value":81},{"type":19,"tag":43,"props":1336,"children":1337},{"style":62},[1338],{"type":24,"value":76},{"type":19,"tag":43,"props":1340,"children":1341},{"style":68},[1342],{"type":24,"value":90},{"type":19,"tag":43,"props":1344,"children":1345},{"style":62},[1346],{"type":24,"value":76},{"type":19,"tag":43,"props":1348,"children":1349},{"style":68},[1350],{"type":24,"value":1351},"\"shift\"",{"type":19,"tag":43,"props":1353,"children":1354},{"style":62},[1355],{"type":24,"value":1356},"}, ",{"type":19,"tag":43,"props":1358,"children":1359},{"style":68},[1360],{"type":24,"value":899},{"type":19,"tag":43,"props":1362,"children":1363},{"style":62},[1364],{"type":24,"value":76},{"type":19,"tag":43,"props":1366,"children":1367},{"style":50},[1368],{"type":24,"value":908},{"type":19,"tag":43,"props":1370,"children":1371},{"style":62},[1372],{"type":24,"value":913},{"type":19,"tag":43,"props":1374,"children":1375},{"class":45,"line":98},[1376,1380,1384,1389],{"type":19,"tag":43,"props":1377,"children":1378},{"style":131},[1379],{"type":24,"value":922},{"type":19,"tag":43,"props":1381,"children":1382},{"style":62},[1383],{"type":24,"value":927},{"type":19,"tag":43,"props":1385,"children":1386},{"style":930},[1387],{"type":24,"value":1388},"5",{"type":19,"tag":43,"props":1390,"children":1391},{"style":62},[1392],{"type":24,"value":150},{"type":19,"tag":43,"props":1394,"children":1395},{"class":45,"line":108},[1396,1400],{"type":19,"tag":43,"props":1397,"children":1398},{"style":50},[1399],{"type":24,"value":946},{"type":19,"tag":43,"props":1401,"children":1402},{"style":62},[1403],{"type":24,"value":150},{"type":19,"tag":43,"props":1405,"children":1406},{"class":45,"line":118},[1407],{"type":19,"tag":43,"props":1408,"children":1409},{"emptyLinePlaceholder":102},[1410],{"type":24,"value":105},{"type":19,"tag":43,"props":1412,"children":1413},{"class":45,"line":153},[1414,1418,1422,1426,1430,1434,1438,1442,1446,1450,1454,1458,1462,1466,1470,1474],{"type":19,"tag":43,"props":1415,"children":1416},{"style":56},[1417],{"type":24,"value":406},{"type":19,"tag":43,"props":1419,"children":1420},{"style":62},[1421],{"type":24,"value":876},{"type":19,"tag":43,"props":1423,"children":1424},{"style":131},[1425],{"type":24,"value":881},{"type":19,"tag":43,"props":1427,"children":1428},{"style":62},[1429],{"type":24,"value":1322},{"type":19,"tag":43,"props":1431,"children":1432},{"style":68},[1433],{"type":24,"value":71},{"type":19,"tag":43,"props":1435,"children":1436},{"style":62},[1437],{"type":24,"value":76},{"type":19,"tag":43,"props":1439,"children":1440},{"style":68},[1441],{"type":24,"value":81},{"type":19,"tag":43,"props":1443,"children":1444},{"style":62},[1445],{"type":24,"value":76},{"type":19,"tag":43,"props":1447,"children":1448},{"style":68},[1449],{"type":24,"value":90},{"type":19,"tag":43,"props":1451,"children":1452},{"style":62},[1453],{"type":24,"value":76},{"type":19,"tag":43,"props":1455,"children":1456},{"style":68},[1457],{"type":24,"value":1351},{"type":19,"tag":43,"props":1459,"children":1460},{"style":62},[1461],{"type":24,"value":1356},{"type":19,"tag":43,"props":1463,"children":1464},{"style":68},[1465],{"type":24,"value":991},{"type":19,"tag":43,"props":1467,"children":1468},{"style":62},[1469],{"type":24,"value":76},{"type":19,"tag":43,"props":1471,"children":1472},{"style":50},[1473],{"type":24,"value":908},{"type":19,"tag":43,"props":1475,"children":1476},{"style":62},[1477],{"type":24,"value":913},{"type":19,"tag":43,"props":1479,"children":1480},{"class":45,"line":191},[1481,1485,1489,1493],{"type":19,"tag":43,"props":1482,"children":1483},{"style":131},[1484],{"type":24,"value":922},{"type":19,"tag":43,"props":1486,"children":1487},{"style":62},[1488],{"type":24,"value":139},{"type":19,"tag":43,"props":1490,"children":1491},{"style":930},[1492],{"type":24,"value":1388},{"type":19,"tag":43,"props":1494,"children":1495},{"style":62},[1496],{"type":24,"value":150},{"type":19,"tag":43,"props":1498,"children":1499},{"class":45,"line":200},[1500,1504],{"type":19,"tag":43,"props":1501,"children":1502},{"style":50},[1503],{"type":24,"value":946},{"type":19,"tag":43,"props":1505,"children":1506},{"style":62},[1507],{"type":24,"value":150},{"type":19,"tag":1509,"props":1510,"children":1511},"style",{},[1512],{"type":24,"value":1513},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":98,"depth":98,"links":1515},[1516,1517,1518],{"id":1101,"depth":98,"text":1104},{"id":1209,"depth":98,"text":1212},{"id":1271,"depth":98,"text":1274},"markdown","content:blog:universal-media-seek-controls-with-hammerspoon.md","content","blog\u002Funiversal-media-seek-controls-with-hammerspoon.md","blog\u002Funiversal-media-seek-controls-with-hammerspoon","md",[1526,2053],{"_path":1527,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1528,"description":1529,"date":1530,"topics":1531,"body":1533,"_type":1519,"_id":2050,"_source":1521,"_file":2051,"_stem":2052,"_extension":1524},"\u002Fblog\u002Fglobal-hotkey-to-toggle-cmux-with-hammerspoon","Global hotkey to toggle cmux with Hammerspoon","How to set up a global Ctrl+Space hotkey to summon and hide cmux, since cmux does not have a built-in global hotkey feature.","2026-03-14T10:00:00+07:00",[12,13,1532],"productivity",{"type":16,"children":1534,"toc":2045},[1535,1549,1555,1560,1565,1584,1598,1604,1622,1871,1876,1909,1914,1920,1932,2036,2041],{"type":19,"tag":20,"props":1536,"children":1537},{},[1538,1540,1547],{"type":24,"value":1539},"I recently switched to ",{"type":19,"tag":1222,"props":1541,"children":1544},{"href":1542,"rel":1543},"https:\u002F\u002Fwww.cmux.dev\u002F",[1226],[1545],{"type":24,"value":1546},"cmux",{"type":24,"value":1548}," as my main terminal. It's a Ghostty-based macOS terminal built for AI coding agents, with vertical tabs, split panes, and notifications. It's great, but it's missing one feature I consider essential: a global hotkey to summon the terminal from anywhere.",{"type":19,"tag":1099,"props":1550,"children":1552},{"id":1551},"why-ctrlspace",[1553],{"type":24,"value":1554},"Why Ctrl+Space",{"type":19,"tag":20,"props":1556,"children":1557},{},[1558],{"type":24,"value":1559},"Most terminal apps I've used (Ghostty, Windows Terminal, iTerm2) have a built-in option to set a system-wide hotkey that toggles the terminal window. You press the key combo, the terminal appears. Press it again, it hides. This eliminates Cmd+Tab or Alt+Tab cycling.",{"type":19,"tag":20,"props":1561,"children":1562},{},[1563],{"type":24,"value":1564},"I picked Ctrl+Space specifically because:",{"type":19,"tag":1566,"props":1567,"children":1568},"ul",{},[1569,1574,1579],{"type":19,"tag":1123,"props":1570,"children":1571},{},[1572],{"type":24,"value":1573},"It's easy to hit with one hand, right on the home row area.",{"type":19,"tag":1123,"props":1575,"children":1576},{},[1577],{"type":24,"value":1578},"It doesn't conflict with common app shortcuts. macOS used to use it for input source switching, but I have that disabled.",{"type":19,"tag":1123,"props":1580,"children":1581},{},[1582],{"type":24,"value":1583},"It's what I used with Ghostty, Windows Terminal, and iTerm before, so the muscle memory is already there.",{"type":19,"tag":20,"props":1585,"children":1586},{},[1587,1589,1596],{"type":24,"value":1588},"cmux doesn't offer this setting anywhere in its preferences. There's an ",{"type":19,"tag":1222,"props":1590,"children":1593},{"href":1591,"rel":1592},"https:\u002F\u002Fgithub.com\u002Fmanaflow-ai\u002Fcmux\u002Fissues\u002F135",[1226],[1594],{"type":24,"value":1595},"open issue",{"type":24,"value":1597}," requesting customizable keybindings, but as of now it's not available. Terminal keybindings in cmux are read from your Ghostty config, and cmux-specific shortcuts are in its Settings, but neither of those covers a global summon hotkey.",{"type":19,"tag":1099,"props":1599,"children":1601},{"id":1600},"the-hammerspoon-solution",[1602],{"type":24,"value":1603},"The Hammerspoon solution",{"type":19,"tag":20,"props":1605,"children":1606},{},[1607,1609,1614,1616,1621],{"type":24,"value":1608},"Since I already use ",{"type":19,"tag":1222,"props":1610,"children":1612},{"href":1224,"rel":1611},[1226],[1613],{"type":24,"value":1229},{"type":24,"value":1615}," for other macOS automation, adding a global hotkey for cmux is just a few lines in ",{"type":19,"tag":39,"props":1617,"children":1619},{"className":1618},[],[1620],{"type":24,"value":1254},{"type":24,"value":457},{"type":19,"tag":32,"props":1623,"children":1625},{"className":34,"code":1624,"language":36,"meta":7,"style":7},"hs.hotkey.bind({\"ctrl\"}, \"space\", function()\n    local app = hs.application.find(\"cmux\")\n    if app then\n        if app:isFrontmost() then\n            app:hide()\n        else\n            app:activate()\n        end\n    else\n        hs.application.launchOrFocus(\"cmux\")\n    end\nend)\n",[1626],{"type":19,"tag":39,"props":1627,"children":1628},{"__ignoreMap":7},[1629,1673,1715,1731,1760,1781,1789,1809,1817,1825,1853,1860],{"type":19,"tag":43,"props":1630,"children":1631},{"class":45,"line":46},[1632,1636,1640,1644,1648,1652,1656,1661,1665,1669],{"type":19,"tag":43,"props":1633,"children":1634},{"style":56},[1635],{"type":24,"value":406},{"type":19,"tag":43,"props":1637,"children":1638},{"style":62},[1639],{"type":24,"value":876},{"type":19,"tag":43,"props":1641,"children":1642},{"style":131},[1643],{"type":24,"value":881},{"type":19,"tag":43,"props":1645,"children":1646},{"style":62},[1647],{"type":24,"value":1322},{"type":19,"tag":43,"props":1649,"children":1650},{"style":68},[1651],{"type":24,"value":90},{"type":19,"tag":43,"props":1653,"children":1654},{"style":62},[1655],{"type":24,"value":1356},{"type":19,"tag":43,"props":1657,"children":1658},{"style":68},[1659],{"type":24,"value":1660},"\"space\"",{"type":19,"tag":43,"props":1662,"children":1663},{"style":62},[1664],{"type":24,"value":76},{"type":19,"tag":43,"props":1666,"children":1667},{"style":50},[1668],{"type":24,"value":908},{"type":19,"tag":43,"props":1670,"children":1671},{"style":62},[1672],{"type":24,"value":913},{"type":19,"tag":43,"props":1674,"children":1675},{"class":45,"line":98},[1676,1680,1685,1689,1693,1697,1702,1706,1711],{"type":19,"tag":43,"props":1677,"children":1678},{"style":50},[1679],{"type":24,"value":391},{"type":19,"tag":43,"props":1681,"children":1682},{"style":56},[1683],{"type":24,"value":1684}," app",{"type":19,"tag":43,"props":1686,"children":1687},{"style":62},[1688],{"type":24,"value":401},{"type":19,"tag":43,"props":1690,"children":1691},{"style":56},[1692],{"type":24,"value":406},{"type":19,"tag":43,"props":1694,"children":1695},{"style":62},[1696],{"type":24,"value":411},{"type":19,"tag":43,"props":1698,"children":1699},{"style":131},[1700],{"type":24,"value":1701},"find",{"type":19,"tag":43,"props":1703,"children":1704},{"style":62},[1705],{"type":24,"value":139},{"type":19,"tag":43,"props":1707,"children":1708},{"style":68},[1709],{"type":24,"value":1710},"\"cmux\"",{"type":19,"tag":43,"props":1712,"children":1713},{"style":62},[1714],{"type":24,"value":150},{"type":19,"tag":43,"props":1716,"children":1717},{"class":45,"line":108},[1718,1722,1726],{"type":19,"tag":43,"props":1719,"children":1720},{"style":50},[1721],{"type":24,"value":438},{"type":19,"tag":43,"props":1723,"children":1724},{"style":56},[1725],{"type":24,"value":1684},{"type":19,"tag":43,"props":1727,"children":1728},{"style":50},[1729],{"type":24,"value":1730}," then\n",{"type":19,"tag":43,"props":1732,"children":1733},{"class":45,"line":118},[1734,1739,1743,1747,1752,1756],{"type":19,"tag":43,"props":1735,"children":1736},{"style":50},[1737],{"type":24,"value":1738},"        if",{"type":19,"tag":43,"props":1740,"children":1741},{"style":450},[1742],{"type":24,"value":1684},{"type":19,"tag":43,"props":1744,"children":1745},{"style":62},[1746],{"type":24,"value":457},{"type":19,"tag":43,"props":1748,"children":1749},{"style":131},[1750],{"type":24,"value":1751},"isFrontmost",{"type":19,"tag":43,"props":1753,"children":1754},{"style":62},[1755],{"type":24,"value":467},{"type":19,"tag":43,"props":1757,"children":1758},{"style":50},[1759],{"type":24,"value":506},{"type":19,"tag":43,"props":1761,"children":1762},{"class":45,"line":153},[1763,1768,1772,1777],{"type":19,"tag":43,"props":1764,"children":1765},{"style":450},[1766],{"type":24,"value":1767},"            app",{"type":19,"tag":43,"props":1769,"children":1770},{"style":62},[1771],{"type":24,"value":457},{"type":19,"tag":43,"props":1773,"children":1774},{"style":131},[1775],{"type":24,"value":1776},"hide",{"type":19,"tag":43,"props":1778,"children":1779},{"style":62},[1780],{"type":24,"value":913},{"type":19,"tag":43,"props":1782,"children":1783},{"class":45,"line":191},[1784],{"type":19,"tag":43,"props":1785,"children":1786},{"style":50},[1787],{"type":24,"value":1788},"        else\n",{"type":19,"tag":43,"props":1790,"children":1791},{"class":45,"line":200},[1792,1796,1800,1805],{"type":19,"tag":43,"props":1793,"children":1794},{"style":450},[1795],{"type":24,"value":1767},{"type":19,"tag":43,"props":1797,"children":1798},{"style":62},[1799],{"type":24,"value":457},{"type":19,"tag":43,"props":1801,"children":1802},{"style":131},[1803],{"type":24,"value":1804},"activate",{"type":19,"tag":43,"props":1806,"children":1807},{"style":62},[1808],{"type":24,"value":913},{"type":19,"tag":43,"props":1810,"children":1811},{"class":45,"line":209},[1812],{"type":19,"tag":43,"props":1813,"children":1814},{"style":50},[1815],{"type":24,"value":1816},"        end\n",{"type":19,"tag":43,"props":1818,"children":1819},{"class":45,"line":218},[1820],{"type":19,"tag":43,"props":1821,"children":1822},{"style":50},[1823],{"type":24,"value":1824},"    else\n",{"type":19,"tag":43,"props":1826,"children":1827},{"class":45,"line":227},[1828,1832,1836,1841,1845,1849],{"type":19,"tag":43,"props":1829,"children":1830},{"style":56},[1831],{"type":24,"value":515},{"type":19,"tag":43,"props":1833,"children":1834},{"style":62},[1835],{"type":24,"value":411},{"type":19,"tag":43,"props":1837,"children":1838},{"style":131},[1839],{"type":24,"value":1840},"launchOrFocus",{"type":19,"tag":43,"props":1842,"children":1843},{"style":62},[1844],{"type":24,"value":139},{"type":19,"tag":43,"props":1846,"children":1847},{"style":68},[1848],{"type":24,"value":1710},{"type":19,"tag":43,"props":1850,"children":1851},{"style":62},[1852],{"type":24,"value":150},{"type":19,"tag":43,"props":1854,"children":1855},{"class":45,"line":236},[1856],{"type":19,"tag":43,"props":1857,"children":1858},{"style":50},[1859],{"type":24,"value":572},{"type":19,"tag":43,"props":1861,"children":1862},{"class":45,"line":245},[1863,1867],{"type":19,"tag":43,"props":1864,"children":1865},{"style":50},[1866],{"type":24,"value":946},{"type":19,"tag":43,"props":1868,"children":1869},{"style":62},[1870],{"type":24,"value":150},{"type":19,"tag":20,"props":1872,"children":1873},{},[1874],{"type":24,"value":1875},"This does three things depending on the current state:",{"type":19,"tag":1119,"props":1877,"children":1878},{},[1879,1889,1899],{"type":19,"tag":1123,"props":1880,"children":1881},{},[1882,1887],{"type":19,"tag":1127,"props":1883,"children":1884},{},[1885],{"type":24,"value":1886},"cmux is focused",{"type":24,"value":1888}," - hides it, sending you back to whatever app you were using.",{"type":19,"tag":1123,"props":1890,"children":1891},{},[1892,1897],{"type":19,"tag":1127,"props":1893,"children":1894},{},[1895],{"type":24,"value":1896},"cmux is running but not focused",{"type":24,"value":1898}," - brings it to the front.",{"type":19,"tag":1123,"props":1900,"children":1901},{},[1902,1907],{"type":19,"tag":1127,"props":1903,"children":1904},{},[1905],{"type":24,"value":1906},"cmux is not running",{"type":24,"value":1908}," - launches it.",{"type":19,"tag":20,"props":1910,"children":1911},{},[1912],{"type":24,"value":1913},"After adding the snippet, reload Hammerspoon (click the menu bar icon and select \"Reload Config\", or set up a reload hotkey).",{"type":19,"tag":1099,"props":1915,"children":1917},{"id":1916},"works-with-any-app",[1918],{"type":24,"value":1919},"Works with any app",{"type":19,"tag":20,"props":1921,"children":1922},{},[1923,1925,1930],{"type":24,"value":1924},"There's nothing cmux-specific about this pattern. You can use the same snippet for any app by replacing ",{"type":19,"tag":39,"props":1926,"children":1928},{"className":1927},[],[1929],{"type":24,"value":1710},{"type":24,"value":1931}," with the app's process name. To find the exact process name, run this in the Hammerspoon console:",{"type":19,"tag":32,"props":1933,"children":1935},{"className":34,"code":1934,"language":36,"meta":7,"style":7},"hs.fnutils.each(hs.application.runningApplications(), function(app)\n    print(app:name())\nend)\n",[1936],{"type":19,"tag":39,"props":1937,"children":1938},{"__ignoreMap":7},[1939,1995,2025],{"type":19,"tag":43,"props":1940,"children":1941},{"class":45,"line":46},[1942,1946,1951,1956,1960,1964,1968,1973,1978,1982,1986,1991],{"type":19,"tag":43,"props":1943,"children":1944},{"style":56},[1945],{"type":24,"value":406},{"type":19,"tag":43,"props":1947,"children":1948},{"style":62},[1949],{"type":24,"value":1950},".fnutils.",{"type":19,"tag":43,"props":1952,"children":1953},{"style":131},[1954],{"type":24,"value":1955},"each",{"type":19,"tag":43,"props":1957,"children":1958},{"style":62},[1959],{"type":24,"value":139},{"type":19,"tag":43,"props":1961,"children":1962},{"style":56},[1963],{"type":24,"value":406},{"type":19,"tag":43,"props":1965,"children":1966},{"style":62},[1967],{"type":24,"value":411},{"type":19,"tag":43,"props":1969,"children":1970},{"style":131},[1971],{"type":24,"value":1972},"runningApplications",{"type":19,"tag":43,"props":1974,"children":1975},{"style":62},[1976],{"type":24,"value":1977},"(), ",{"type":19,"tag":43,"props":1979,"children":1980},{"style":50},[1981],{"type":24,"value":908},{"type":19,"tag":43,"props":1983,"children":1984},{"style":62},[1985],{"type":24,"value":139},{"type":19,"tag":43,"props":1987,"children":1988},{"style":142},[1989],{"type":24,"value":1990},"app",{"type":19,"tag":43,"props":1992,"children":1993},{"style":62},[1994],{"type":24,"value":150},{"type":19,"tag":43,"props":1996,"children":1997},{"class":45,"line":98},[1998,2003,2007,2011,2015,2020],{"type":19,"tag":43,"props":1999,"children":2000},{"style":176},[2001],{"type":24,"value":2002},"    print",{"type":19,"tag":43,"props":2004,"children":2005},{"style":62},[2006],{"type":24,"value":139},{"type":19,"tag":43,"props":2008,"children":2009},{"style":450},[2010],{"type":24,"value":1990},{"type":19,"tag":43,"props":2012,"children":2013},{"style":62},[2014],{"type":24,"value":457},{"type":19,"tag":43,"props":2016,"children":2017},{"style":131},[2018],{"type":24,"value":2019},"name",{"type":19,"tag":43,"props":2021,"children":2022},{"style":62},[2023],{"type":24,"value":2024},"())\n",{"type":19,"tag":43,"props":2026,"children":2027},{"class":45,"line":108},[2028,2032],{"type":19,"tag":43,"props":2029,"children":2030},{"style":50},[2031],{"type":24,"value":946},{"type":19,"tag":43,"props":2033,"children":2034},{"style":62},[2035],{"type":24,"value":150},{"type":19,"tag":20,"props":2037,"children":2038},{},[2039],{"type":24,"value":2040},"Or check Activity Monitor.",{"type":19,"tag":1509,"props":2042,"children":2043},{},[2044],{"type":24,"value":1513},{"title":7,"searchDepth":98,"depth":98,"links":2046},[2047,2048,2049],{"id":1551,"depth":98,"text":1554},{"id":1600,"depth":98,"text":1603},{"id":1916,"depth":98,"text":1919},"content:blog:global-hotkey-to-toggle-cmux-with-hammerspoon.md","blog\u002Fglobal-hotkey-to-toggle-cmux-with-hammerspoon.md","blog\u002Fglobal-hotkey-to-toggle-cmux-with-hammerspoon",{"_path":2054,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":2055,"description":2056,"date":2057,"topics":2058,"body":2062,"_type":1519,"_id":3762,"_source":1521,"_file":3763,"_stem":3764,"_extension":1524},"\u002Fblog\u002Fbetter-local-development-with-localhost-subdomains","Better local development with .localhost subdomains","How .localhost subdomains can help smooth out cookie, CORS, and HTTPS issues in local development.","2025-06-28T10:21:00+07:00",[2059,2060,2061],"local-dev","web","networking",{"type":16,"children":2063,"toc":3737},[2064,2077,2088,2094,2120,2127,2133,2138,2185,2197,2202,2213,2224,2231,2252,2257,2293,2299,2316,2348,2361,2373,2414,2419,2463,2469,2504,2509,2542,2547,2593,2599,2631,2797,2817,2823,2833,2879,2885,3009,3015,3176,3182,3406,3412,3418,3423,3544,3550,3583,3589,3666,3672,3682,3733],{"type":19,"tag":20,"props":2065,"children":2066},{},[2067,2069,2075],{"type":24,"value":2068},"If you're building web apps with multiple services running locally, like a frontend, an API, maybe an auth service, you're probably using ",{"type":19,"tag":39,"props":2070,"children":2072},{"className":2071},[],[2073],{"type":24,"value":2074},"localhost",{"type":24,"value":2076}," with different ports. It works, but there are some rough edges once cookies, CORS, or HTTPS get involved.",{"type":19,"tag":20,"props":2078,"children":2079},{},[2080,2086],{"type":19,"tag":39,"props":2081,"children":2083},{"className":2082},[],[2084],{"type":24,"value":2085},".localhost",{"type":24,"value":2087}," subdomains can help smooth things out.",{"type":19,"tag":1099,"props":2089,"children":2091},{"id":2090},"how-localhost-works",[2092],{"type":24,"value":2093},"How .localhost works",{"type":19,"tag":20,"props":2095,"children":2096},{},[2097,2118],{"type":19,"tag":1127,"props":2098,"children":2099},{},[2100,2102,2108,2110,2116],{"type":24,"value":2101},"Any subdomain under ",{"type":19,"tag":39,"props":2103,"children":2105},{"className":2104},[],[2106],{"type":24,"value":2107},"*.localhost",{"type":24,"value":2109}," resolves to ",{"type":19,"tag":39,"props":2111,"children":2113},{"className":2112},[],[2114],{"type":24,"value":2115},"127.0.0.1",{"type":24,"value":2117}," (your local machine)",{"type":24,"value":2119},", on macOS, Linux, and Windows, without any configuration.",{"type":19,"tag":32,"props":2121,"children":2124},{"className":2122},[2123],"mermaid",[2125],{"type":24,"value":2126},"graph TD\n    A[web.localhost:3000] --> B[127.0.0.1:3000]\n    C[anything.you.want.localhost:3001] --> D[127.0.0.1:3001]\n    E[auth.localhost:5000] --> F[127.0.0.1:5000]\n    G[payments.localhost:5002] --> H[127.0.0.1:5002]\n\n    B --> I[Frontend App]\n    D --> J[Backend API]\n    F --> K[Auth Service]\n    H --> L[Webhook Listener]\n\n    classDef domainNode fill:#2d2d2d,stroke:#14b8a6,stroke-width:2px,color:#e4e4e4\n    classDef ipNode fill:#343030,stroke:#f59e0b,stroke-width:2px,color:#e4e4e4\n    classDef serviceNode fill:#1f1f1f,stroke:#f97316,stroke-width:2px,color:#e4e4e4\n\n    class A,C,E,G domainNode\n    class B,D,F,H ipNode\n    class I,J,K,L serviceNode\n",{"type":19,"tag":1099,"props":2128,"children":2130},{"id":2129},"clean-multi-service-dev-environments",[2131],{"type":24,"value":2132},"Clean multi-service dev environments",{"type":19,"tag":20,"props":2134,"children":2135},{},[2136],{"type":24,"value":2137},"Instead of remembering which port maps to which service, you can use subdomains:",{"type":19,"tag":1566,"props":2139,"children":2140},{},[2141,2152,2163,2174],{"type":19,"tag":1123,"props":2142,"children":2143},{},[2144,2150],{"type":19,"tag":39,"props":2145,"children":2147},{"className":2146},[],[2148],{"type":24,"value":2149},"web.localhost:3000",{"type":24,"value":2151}," -> your frontend",{"type":19,"tag":1123,"props":2153,"children":2154},{},[2155,2161],{"type":19,"tag":39,"props":2156,"children":2158},{"className":2157},[],[2159],{"type":24,"value":2160},"api.localhost:3001",{"type":24,"value":2162}," -> backend service",{"type":19,"tag":1123,"props":2164,"children":2165},{},[2166,2172],{"type":19,"tag":39,"props":2167,"children":2169},{"className":2168},[],[2170],{"type":24,"value":2171},"auth.localhost:5000",{"type":24,"value":2173}," -> auth or mock SSO",{"type":19,"tag":1123,"props":2175,"children":2176},{},[2177,2183],{"type":19,"tag":39,"props":2178,"children":2180},{"className":2179},[],[2181],{"type":24,"value":2182},"payments.localhost:5002",{"type":24,"value":2184}," -> webhook listener",{"type":19,"tag":20,"props":2186,"children":2187},{},[2188,2190,2195],{"type":24,"value":2189},"All of these resolve to ",{"type":19,"tag":39,"props":2191,"children":2193},{"className":2192},[],[2194],{"type":24,"value":2115},{"type":24,"value":2196},". You can route them via your dev proxy (Vite, Webpack, Traefik) or just hardcode ports.",{"type":19,"tag":20,"props":2198,"children":2199},{},[2200],{"type":24,"value":2201},"The URLs end up looking closer to production, which makes configuration a bit more straightforward.",{"type":19,"tag":1099,"props":2203,"children":2205},{"id":2204},"cookie-behavior-on-localhost",[2206,2208],{"type":24,"value":2207},"Cookie behavior on ",{"type":19,"tag":39,"props":2209,"children":2211},{"className":2210},[],[2212],{"type":24,"value":2074},{"type":19,"tag":20,"props":2214,"children":2215},{},[2216,2218,2223],{"type":24,"value":2217},"There are a few things worth knowing about how cookies work on ",{"type":19,"tag":39,"props":2219,"children":2221},{"className":2220},[],[2222],{"type":24,"value":2074},{"type":24,"value":1243},{"type":19,"tag":2225,"props":2226,"children":2228},"h3",{"id":2227},"ports-do-not-separate-cookies",[2229],{"type":24,"value":2230},"Ports do not separate cookies",{"type":19,"tag":20,"props":2232,"children":2233},{},[2234,2236,2242,2244,2250],{"type":24,"value":2235},"Browsers don't consider the port when scoping cookies. A cookie set on ",{"type":19,"tag":39,"props":2237,"children":2239},{"className":2238},[],[2240],{"type":24,"value":2241},"localhost:3000",{"type":24,"value":2243}," gets sent to ",{"type":19,"tag":39,"props":2245,"children":2247},{"className":2246},[],[2248],{"type":24,"value":2249},"localhost:3001",{"type":24,"value":2251}," too. In production, your services would be on separate domains or subdomains with their own cookie space. On localhost, they all share one.",{"type":19,"tag":20,"props":2253,"children":2254},{},[2255],{"type":24,"value":2256},"This can lead to:",{"type":19,"tag":1566,"props":2258,"children":2259},{},[2260,2265],{"type":19,"tag":1123,"props":2261,"children":2262},{},[2263],{"type":24,"value":2264},"Cookies being shared across unrelated services",{"type":19,"tag":1123,"props":2266,"children":2267},{},[2268,2270,2276,2277,2283,2285,2291],{"type":24,"value":2269},"Difficulty testing ",{"type":19,"tag":39,"props":2271,"children":2273},{"className":2272},[],[2274],{"type":24,"value":2275},"Domain",{"type":24,"value":76},{"type":19,"tag":39,"props":2278,"children":2280},{"className":2279},[],[2281],{"type":24,"value":2282},"Path",{"type":24,"value":2284},", or ",{"type":19,"tag":39,"props":2286,"children":2288},{"className":2287},[],[2289],{"type":24,"value":2290},"SameSite",{"type":24,"value":2292}," behavior accurately",{"type":19,"tag":32,"props":2294,"children":2296},{"className":2295},[2123],[2297],{"type":24,"value":2298},"graph TB\n    A[localhost:3000] --> B[Cookie Jar]\n    C[localhost:3001] --> B\n    D[localhost:5000] --> B\n    E[localhost:5002] --> B\n\n    classDef portNode fill:#2d2d2d,stroke:#14b8a6,stroke-width:2px,color:#e4e4e4\n    classDef cookieNode fill:#f59e0b,stroke:#f59e0b,stroke-width:2px,color:#1a1a1a\n\n    class A,C,D,E portNode\n    class B cookieNode\n",{"type":19,"tag":2225,"props":2300,"children":2302},{"id":2301},"_127001-and-localhost-are-not-treated-the-same",[2303,2308,2309,2314],{"type":19,"tag":39,"props":2304,"children":2306},{"className":2305},[],[2307],{"type":24,"value":2115},{"type":24,"value":1287},{"type":19,"tag":39,"props":2310,"children":2312},{"className":2311},[],[2313],{"type":24,"value":2074},{"type":24,"value":2315}," are not treated the same",{"type":19,"tag":20,"props":2317,"children":2318},{},[2319,2321,2326,2327,2332,2334,2339,2341,2347],{"type":24,"value":2320},"Although ",{"type":19,"tag":39,"props":2322,"children":2324},{"className":2323},[],[2325],{"type":24,"value":2115},{"type":24,"value":1287},{"type":19,"tag":39,"props":2328,"children":2330},{"className":2329},[],[2331],{"type":24,"value":2074},{"type":24,"value":2333}," both point to your local machine, browsers treat them as different origins. A cookie set on ",{"type":19,"tag":39,"props":2335,"children":2337},{"className":2336},[],[2338],{"type":24,"value":2241},{"type":24,"value":2340}," won't be sent with requests to ",{"type":19,"tag":39,"props":2342,"children":2344},{"className":2343},[],[2345],{"type":24,"value":2346},"127.0.0.1:3001",{"type":24,"value":1243},{"type":19,"tag":2225,"props":2349,"children":2351},{"id":2350},"using-localhost-subdomains",[2352,2354,2359],{"type":24,"value":2353},"Using ",{"type":19,"tag":39,"props":2355,"children":2357},{"className":2356},[],[2358],{"type":24,"value":2085},{"type":24,"value":2360}," subdomains",{"type":19,"tag":20,"props":2362,"children":2363},{},[2364,2366,2371],{"type":24,"value":2365},"With ",{"type":19,"tag":39,"props":2367,"children":2369},{"className":2368},[],[2370],{"type":24,"value":2085},{"type":24,"value":2372}," subdomains, each service gets its own domain, which means:",{"type":19,"tag":1566,"props":2374,"children":2375},{},[2376,2381,2392,2409],{"type":19,"tag":1123,"props":2377,"children":2378},{},[2379],{"type":24,"value":2380},"Domain separation for cookies",{"type":19,"tag":1123,"props":2382,"children":2383},{},[2384,2386],{"type":24,"value":2385},"Cookie sharing when you want it, via ",{"type":19,"tag":39,"props":2387,"children":2389},{"className":2388},[],[2390],{"type":24,"value":2391},"Domain=.localhost",{"type":19,"tag":1123,"props":2393,"children":2394},{},[2395,2400,2401,2407],{"type":19,"tag":39,"props":2396,"children":2398},{"className":2397},[],[2399],{"type":24,"value":2290},{"type":24,"value":1287},{"type":19,"tag":39,"props":2402,"children":2404},{"className":2403},[],[2405],{"type":24,"value":2406},"Secure",{"type":24,"value":2408}," flags work as expected",{"type":19,"tag":1123,"props":2410,"children":2411},{},[2412],{"type":24,"value":2413},"CORS behaves like it would in production",{"type":19,"tag":20,"props":2415,"children":2416},{},[2417],{"type":24,"value":2418},"Some additional benefits:",{"type":19,"tag":1566,"props":2420,"children":2421},{},[2422,2440,2458],{"type":19,"tag":1123,"props":2423,"children":2424},{},[2425,2431,2432,2438],{"type":19,"tag":39,"props":2426,"children":2428},{"className":2427},[],[2429],{"type":24,"value":2430},"localStorage",{"type":24,"value":1287},{"type":19,"tag":39,"props":2433,"children":2435},{"className":2434},[],[2436],{"type":24,"value":2437},"sessionStorage",{"type":24,"value":2439}," are isolated by subdomain, not by port",{"type":19,"tag":1123,"props":2441,"children":2442},{},[2443,2449,2451,2456],{"type":19,"tag":39,"props":2444,"children":2446},{"className":2445},[],[2447],{"type":24,"value":2448},"SameSite=None",{"type":24,"value":2450}," requires HTTPS, which ",{"type":19,"tag":39,"props":2452,"children":2454},{"className":2453},[],[2455],{"type":24,"value":2085},{"type":24,"value":2457}," supports with mkcert",{"type":19,"tag":1123,"props":2459,"children":2460},{},[2461],{"type":24,"value":2462},"URLs resemble your production setup",{"type":19,"tag":1099,"props":2464,"children":2466},{"id":2465},"reserved-by-spec-supported-everywhere",[2467],{"type":24,"value":2468},"Reserved by spec, supported everywhere",{"type":19,"tag":20,"props":2470,"children":2471},{},[2472,2474,2481,2482,2487,2489,2494,2496,2502],{"type":24,"value":2473},"Per ",{"type":19,"tag":1222,"props":2475,"children":2478},{"href":2476,"rel":2477},"https:\u002F\u002Fwww.rfc-editor.org\u002Frfc\u002Frfc6761.html",[1226],[2479],{"type":24,"value":2480},"RFC 6761",{"type":24,"value":76},{"type":19,"tag":39,"props":2483,"children":2485},{"className":2484},[],[2486],{"type":24,"value":2085},{"type":24,"value":2488}," is a reserved TLD. It resolves to ",{"type":19,"tag":39,"props":2490,"children":2492},{"className":2491},[],[2493],{"type":24,"value":2115},{"type":24,"value":2495}," or ",{"type":19,"tag":39,"props":2497,"children":2499},{"className":2498},[],[2500],{"type":24,"value":2501},"::1",{"type":24,"value":2503}," without hitting external DNS servers.",{"type":19,"tag":20,"props":2505,"children":2506},{},[2507],{"type":24,"value":2508},"This works on all major operating systems:",{"type":19,"tag":1566,"props":2510,"children":2511},{},[2512,2522,2532],{"type":19,"tag":1123,"props":2513,"children":2514},{},[2515,2520],{"type":19,"tag":1127,"props":2516,"children":2517},{},[2518],{"type":24,"value":2519},"macOS",{"type":24,"value":2521},": Built-in DNS resolver",{"type":19,"tag":1123,"props":2523,"children":2524},{},[2525,2530],{"type":19,"tag":1127,"props":2526,"children":2527},{},[2528],{"type":24,"value":2529},"Linux",{"type":24,"value":2531},": Systemd-resolved and others",{"type":19,"tag":1123,"props":2533,"children":2534},{},[2535,2540],{"type":19,"tag":1127,"props":2536,"children":2537},{},[2538],{"type":24,"value":2539},"Windows",{"type":24,"value":2541},": Windows DNS resolver",{"type":19,"tag":20,"props":2543,"children":2544},{},[2545],{"type":24,"value":2546},"Some things this gives you:",{"type":19,"tag":1566,"props":2548,"children":2549},{},[2550,2567,2577],{"type":19,"tag":1123,"props":2551,"children":2552},{},[2553,2558,2560,2565],{"type":19,"tag":1127,"props":2554,"children":2555},{},[2556],{"type":24,"value":2557},"No DNS leaks.",{"type":24,"value":2559}," ",{"type":19,"tag":39,"props":2561,"children":2563},{"className":2562},[],[2564],{"type":24,"value":2085},{"type":24,"value":2566}," stays local even if your VPN or DNS config is off.",{"type":19,"tag":1123,"props":2568,"children":2569},{},[2570,2575],{"type":19,"tag":1127,"props":2571,"children":2572},{},[2573],{"type":24,"value":2574},"No external traffic.",{"type":24,"value":2576}," Requests stay on your machine.",{"type":19,"tag":1123,"props":2578,"children":2579},{},[2580,2585,2586,2591],{"type":19,"tag":1127,"props":2581,"children":2582},{},[2583],{"type":24,"value":2584},"No conflicts.",{"type":24,"value":2559},{"type":19,"tag":39,"props":2587,"children":2589},{"className":2588},[],[2590],{"type":24,"value":2085},{"type":24,"value":2592}," is unregistrable as a real domain.",{"type":19,"tag":1099,"props":2594,"children":2596},{"id":2595},"tls-support-with-mkcert",[2597],{"type":24,"value":2598},"TLS support with mkcert",{"type":19,"tag":20,"props":2600,"children":2601},{},[2602,2604,2609,2611,2622,2624,2629],{"type":24,"value":2603},"Some cookie features (like ",{"type":19,"tag":39,"props":2605,"children":2607},{"className":2606},[],[2608],{"type":24,"value":2448},{"type":24,"value":2610},") require HTTPS. ",{"type":19,"tag":1222,"props":2612,"children":2615},{"href":2613,"rel":2614},"https:\u002F\u002Fgithub.com\u002FFiloSottile\u002Fmkcert",[1226],[2616],{"type":19,"tag":39,"props":2617,"children":2619},{"className":2618},[],[2620],{"type":24,"value":2621},"mkcert",{"type":24,"value":2623}," makes it easy to generate trusted certificates for ",{"type":19,"tag":39,"props":2625,"children":2627},{"className":2626},[],[2628],{"type":24,"value":2107},{"type":24,"value":2630}," domains:",{"type":19,"tag":32,"props":2632,"children":2636},{"className":2633,"code":2634,"language":2635,"meta":7,"style":7},"language-bash shiki shiki-themes one-dark-pro","# Install mkcert\nbrew install mkcert  # macOS\n# or\nsudo apt install mkcert  # Ubuntu\u002FDebian\n# or\nchoco install mkcert  # Windows (Chocolatey)\n\n# Install the local CA\nmkcert -install\n\n# Generate certificates for your domains\nmkcert web.localhost api.localhost auth.localhost\n","bash",[2637],{"type":19,"tag":39,"props":2638,"children":2639},{"__ignoreMap":7},[2640,2648,2671,2679,2705,2712,2733,2740,2748,2760,2767,2775],{"type":19,"tag":43,"props":2641,"children":2642},{"class":45,"line":46},[2643],{"type":19,"tag":43,"props":2644,"children":2645},{"style":112},[2646],{"type":24,"value":2647},"# Install mkcert\n",{"type":19,"tag":43,"props":2649,"children":2650},{"class":45,"line":98},[2651,2656,2661,2666],{"type":19,"tag":43,"props":2652,"children":2653},{"style":131},[2654],{"type":24,"value":2655},"brew",{"type":19,"tag":43,"props":2657,"children":2658},{"style":68},[2659],{"type":24,"value":2660}," install",{"type":19,"tag":43,"props":2662,"children":2663},{"style":68},[2664],{"type":24,"value":2665}," mkcert",{"type":19,"tag":43,"props":2667,"children":2668},{"style":112},[2669],{"type":24,"value":2670},"  # macOS\n",{"type":19,"tag":43,"props":2672,"children":2673},{"class":45,"line":108},[2674],{"type":19,"tag":43,"props":2675,"children":2676},{"style":112},[2677],{"type":24,"value":2678},"# or\n",{"type":19,"tag":43,"props":2680,"children":2681},{"class":45,"line":118},[2682,2687,2692,2696,2700],{"type":19,"tag":43,"props":2683,"children":2684},{"style":131},[2685],{"type":24,"value":2686},"sudo",{"type":19,"tag":43,"props":2688,"children":2689},{"style":68},[2690],{"type":24,"value":2691}," apt",{"type":19,"tag":43,"props":2693,"children":2694},{"style":68},[2695],{"type":24,"value":2660},{"type":19,"tag":43,"props":2697,"children":2698},{"style":68},[2699],{"type":24,"value":2665},{"type":19,"tag":43,"props":2701,"children":2702},{"style":112},[2703],{"type":24,"value":2704},"  # Ubuntu\u002FDebian\n",{"type":19,"tag":43,"props":2706,"children":2707},{"class":45,"line":153},[2708],{"type":19,"tag":43,"props":2709,"children":2710},{"style":112},[2711],{"type":24,"value":2678},{"type":19,"tag":43,"props":2713,"children":2714},{"class":45,"line":191},[2715,2720,2724,2728],{"type":19,"tag":43,"props":2716,"children":2717},{"style":131},[2718],{"type":24,"value":2719},"choco",{"type":19,"tag":43,"props":2721,"children":2722},{"style":68},[2723],{"type":24,"value":2660},{"type":19,"tag":43,"props":2725,"children":2726},{"style":68},[2727],{"type":24,"value":2665},{"type":19,"tag":43,"props":2729,"children":2730},{"style":112},[2731],{"type":24,"value":2732},"  # Windows (Chocolatey)\n",{"type":19,"tag":43,"props":2734,"children":2735},{"class":45,"line":200},[2736],{"type":19,"tag":43,"props":2737,"children":2738},{"emptyLinePlaceholder":102},[2739],{"type":24,"value":105},{"type":19,"tag":43,"props":2741,"children":2742},{"class":45,"line":209},[2743],{"type":19,"tag":43,"props":2744,"children":2745},{"style":112},[2746],{"type":24,"value":2747},"# Install the local CA\n",{"type":19,"tag":43,"props":2749,"children":2750},{"class":45,"line":218},[2751,2755],{"type":19,"tag":43,"props":2752,"children":2753},{"style":131},[2754],{"type":24,"value":2621},{"type":19,"tag":43,"props":2756,"children":2757},{"style":930},[2758],{"type":24,"value":2759}," -install\n",{"type":19,"tag":43,"props":2761,"children":2762},{"class":45,"line":227},[2763],{"type":19,"tag":43,"props":2764,"children":2765},{"emptyLinePlaceholder":102},[2766],{"type":24,"value":105},{"type":19,"tag":43,"props":2768,"children":2769},{"class":45,"line":236},[2770],{"type":19,"tag":43,"props":2771,"children":2772},{"style":112},[2773],{"type":24,"value":2774},"# Generate certificates for your domains\n",{"type":19,"tag":43,"props":2776,"children":2777},{"class":45,"line":245},[2778,2782,2787,2792],{"type":19,"tag":43,"props":2779,"children":2780},{"style":131},[2781],{"type":24,"value":2621},{"type":19,"tag":43,"props":2783,"children":2784},{"style":68},[2785],{"type":24,"value":2786}," web.localhost",{"type":19,"tag":43,"props":2788,"children":2789},{"style":68},[2790],{"type":24,"value":2791}," api.localhost",{"type":19,"tag":43,"props":2793,"children":2794},{"style":68},[2795],{"type":24,"value":2796}," auth.localhost\n",{"type":19,"tag":20,"props":2798,"children":2799},{},[2800,2802,2808,2809,2815],{"type":24,"value":2801},"This gets you ",{"type":19,"tag":39,"props":2803,"children":2805},{"className":2804},[],[2806],{"type":24,"value":2807},"https:\u002F\u002Fweb.localhost",{"type":24,"value":76},{"type":19,"tag":39,"props":2810,"children":2812},{"className":2811},[],[2813],{"type":24,"value":2814},"https:\u002F\u002Fapi.localhost",{"type":24,"value":2816},", etc. with valid TLS.",{"type":19,"tag":1099,"props":2818,"children":2820},{"id":2819},"works-with-proxies-containers-and-dev-tools",[2821],{"type":24,"value":2822},"Works with proxies, containers, and dev tools",{"type":19,"tag":20,"props":2824,"children":2825},{},[2826,2831],{"type":19,"tag":39,"props":2827,"children":2829},{"className":2828},[],[2830],{"type":24,"value":2085},{"type":24,"value":2832}," fits into existing setups:",{"type":19,"tag":1566,"props":2834,"children":2835},{},[2836,2857,2862,2867],{"type":19,"tag":1123,"props":2837,"children":2838},{},[2839,2841,2847,2849,2855],{"type":24,"value":2840},"Subdomain routing to map ",{"type":19,"tag":39,"props":2842,"children":2844},{"className":2843},[],[2845],{"type":24,"value":2846},"web.localhost",{"type":24,"value":2848}," to one container, ",{"type":19,"tag":39,"props":2850,"children":2852},{"className":2851},[],[2853],{"type":24,"value":2854},"api.localhost",{"type":24,"value":2856}," to another",{"type":19,"tag":1123,"props":2858,"children":2859},{},[2860],{"type":24,"value":2861},"Proxies can route based on host headers",{"type":19,"tag":1123,"props":2863,"children":2864},{},[2865],{"type":24,"value":2866},"Frontend tools like Vite, Next.js, and Create React App support localhost subdomains",{"type":19,"tag":1123,"props":2868,"children":2869},{},[2870,2872,2877],{"type":24,"value":2871},"Test frameworks like Cypress treat ",{"type":19,"tag":39,"props":2873,"children":2875},{"className":2874},[],[2876],{"type":24,"value":2085},{"type":24,"value":2878}," as a trusted domain",{"type":19,"tag":2225,"props":2880,"children":2882},{"id":2881},"example-vite-configuration",[2883],{"type":24,"value":2884},"Example Vite configuration",{"type":19,"tag":32,"props":2886,"children":2890},{"className":2887,"code":2888,"language":2889,"meta":7,"style":7},"language-javascript shiki shiki-themes one-dark-pro","\u002F\u002F vite.config.js\nexport default {\n  server: {\n    host: 'web.localhost',\n    port: 3000,\n    https: true\n  }\n}\n","javascript",[2891],{"type":19,"tag":39,"props":2892,"children":2893},{"__ignoreMap":7},[2894,2902,2920,2933,2956,2977,2994,3002],{"type":19,"tag":43,"props":2895,"children":2896},{"class":45,"line":46},[2897],{"type":19,"tag":43,"props":2898,"children":2899},{"style":112},[2900],{"type":24,"value":2901},"\u002F\u002F vite.config.js\n",{"type":19,"tag":43,"props":2903,"children":2904},{"class":45,"line":98},[2905,2910,2915],{"type":19,"tag":43,"props":2906,"children":2907},{"style":50},[2908],{"type":24,"value":2909},"export",{"type":19,"tag":43,"props":2911,"children":2912},{"style":50},[2913],{"type":24,"value":2914}," default",{"type":19,"tag":43,"props":2916,"children":2917},{"style":62},[2918],{"type":24,"value":2919}," {\n",{"type":19,"tag":43,"props":2921,"children":2922},{"class":45,"line":108},[2923,2928],{"type":19,"tag":43,"props":2924,"children":2925},{"style":56},[2926],{"type":24,"value":2927},"  server",{"type":19,"tag":43,"props":2929,"children":2930},{"style":62},[2931],{"type":24,"value":2932},": {\n",{"type":19,"tag":43,"props":2934,"children":2935},{"class":45,"line":118},[2936,2941,2946,2951],{"type":19,"tag":43,"props":2937,"children":2938},{"style":56},[2939],{"type":24,"value":2940},"    host",{"type":19,"tag":43,"props":2942,"children":2943},{"style":62},[2944],{"type":24,"value":2945},": ",{"type":19,"tag":43,"props":2947,"children":2948},{"style":68},[2949],{"type":24,"value":2950},"'web.localhost'",{"type":19,"tag":43,"props":2952,"children":2953},{"style":62},[2954],{"type":24,"value":2955},",\n",{"type":19,"tag":43,"props":2957,"children":2958},{"class":45,"line":153},[2959,2964,2968,2973],{"type":19,"tag":43,"props":2960,"children":2961},{"style":56},[2962],{"type":24,"value":2963},"    port",{"type":19,"tag":43,"props":2965,"children":2966},{"style":62},[2967],{"type":24,"value":2945},{"type":19,"tag":43,"props":2969,"children":2970},{"style":930},[2971],{"type":24,"value":2972},"3000",{"type":19,"tag":43,"props":2974,"children":2975},{"style":62},[2976],{"type":24,"value":2955},{"type":19,"tag":43,"props":2978,"children":2979},{"class":45,"line":191},[2980,2985,2989],{"type":19,"tag":43,"props":2981,"children":2982},{"style":56},[2983],{"type":24,"value":2984},"    https",{"type":19,"tag":43,"props":2986,"children":2987},{"style":62},[2988],{"type":24,"value":2945},{"type":19,"tag":43,"props":2990,"children":2991},{"style":930},[2992],{"type":24,"value":2993},"true\n",{"type":19,"tag":43,"props":2995,"children":2996},{"class":45,"line":200},[2997],{"type":19,"tag":43,"props":2998,"children":2999},{"style":62},[3000],{"type":24,"value":3001},"  }\n",{"type":19,"tag":43,"props":3003,"children":3004},{"class":45,"line":209},[3005],{"type":19,"tag":43,"props":3006,"children":3007},{"style":62},[3008],{"type":24,"value":95},{"type":19,"tag":2225,"props":3010,"children":3012},{"id":3011},"example-docker-compose-setup",[3013],{"type":24,"value":3014},"Example Docker Compose setup",{"type":19,"tag":32,"props":3016,"children":3020},{"className":3017,"code":3018,"language":3019,"meta":7,"style":7},"language-yaml shiki shiki-themes one-dark-pro","# docker-compose.yml\nversion: '3.8'\nservices:\n  web:\n    image: nginx\n    ports:\n      - \"80:80\"\n    volumes:\n      - .\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf\n    extra_hosts:\n      - \"web.localhost:127.0.0.1\"\n      - \"api.localhost:127.0.0.1\"\n","yaml",[3021],{"type":19,"tag":39,"props":3022,"children":3023},{"__ignoreMap":7},[3024,3032,3049,3062,3074,3091,3103,3116,3128,3140,3152,3164],{"type":19,"tag":43,"props":3025,"children":3026},{"class":45,"line":46},[3027],{"type":19,"tag":43,"props":3028,"children":3029},{"style":112},[3030],{"type":24,"value":3031},"# docker-compose.yml\n",{"type":19,"tag":43,"props":3033,"children":3034},{"class":45,"line":98},[3035,3040,3044],{"type":19,"tag":43,"props":3036,"children":3037},{"style":56},[3038],{"type":24,"value":3039},"version",{"type":19,"tag":43,"props":3041,"children":3042},{"style":62},[3043],{"type":24,"value":2945},{"type":19,"tag":43,"props":3045,"children":3046},{"style":68},[3047],{"type":24,"value":3048},"'3.8'\n",{"type":19,"tag":43,"props":3050,"children":3051},{"class":45,"line":108},[3052,3057],{"type":19,"tag":43,"props":3053,"children":3054},{"style":56},[3055],{"type":24,"value":3056},"services",{"type":19,"tag":43,"props":3058,"children":3059},{"style":62},[3060],{"type":24,"value":3061},":\n",{"type":19,"tag":43,"props":3063,"children":3064},{"class":45,"line":118},[3065,3070],{"type":19,"tag":43,"props":3066,"children":3067},{"style":56},[3068],{"type":24,"value":3069},"  web",{"type":19,"tag":43,"props":3071,"children":3072},{"style":62},[3073],{"type":24,"value":3061},{"type":19,"tag":43,"props":3075,"children":3076},{"class":45,"line":153},[3077,3082,3086],{"type":19,"tag":43,"props":3078,"children":3079},{"style":56},[3080],{"type":24,"value":3081},"    image",{"type":19,"tag":43,"props":3083,"children":3084},{"style":62},[3085],{"type":24,"value":2945},{"type":19,"tag":43,"props":3087,"children":3088},{"style":68},[3089],{"type":24,"value":3090},"nginx\n",{"type":19,"tag":43,"props":3092,"children":3093},{"class":45,"line":191},[3094,3099],{"type":19,"tag":43,"props":3095,"children":3096},{"style":56},[3097],{"type":24,"value":3098},"    ports",{"type":19,"tag":43,"props":3100,"children":3101},{"style":62},[3102],{"type":24,"value":3061},{"type":19,"tag":43,"props":3104,"children":3105},{"class":45,"line":200},[3106,3111],{"type":19,"tag":43,"props":3107,"children":3108},{"style":62},[3109],{"type":24,"value":3110},"      - ",{"type":19,"tag":43,"props":3112,"children":3113},{"style":68},[3114],{"type":24,"value":3115},"\"80:80\"\n",{"type":19,"tag":43,"props":3117,"children":3118},{"class":45,"line":209},[3119,3124],{"type":19,"tag":43,"props":3120,"children":3121},{"style":56},[3122],{"type":24,"value":3123},"    volumes",{"type":19,"tag":43,"props":3125,"children":3126},{"style":62},[3127],{"type":24,"value":3061},{"type":19,"tag":43,"props":3129,"children":3130},{"class":45,"line":218},[3131,3135],{"type":19,"tag":43,"props":3132,"children":3133},{"style":62},[3134],{"type":24,"value":3110},{"type":19,"tag":43,"props":3136,"children":3137},{"style":68},[3138],{"type":24,"value":3139},".\u002Fnginx.conf:\u002Fetc\u002Fnginx\u002Fnginx.conf\n",{"type":19,"tag":43,"props":3141,"children":3142},{"class":45,"line":227},[3143,3148],{"type":19,"tag":43,"props":3144,"children":3145},{"style":56},[3146],{"type":24,"value":3147},"    extra_hosts",{"type":19,"tag":43,"props":3149,"children":3150},{"style":62},[3151],{"type":24,"value":3061},{"type":19,"tag":43,"props":3153,"children":3154},{"class":45,"line":236},[3155,3159],{"type":19,"tag":43,"props":3156,"children":3157},{"style":62},[3158],{"type":24,"value":3110},{"type":19,"tag":43,"props":3160,"children":3161},{"style":68},[3162],{"type":24,"value":3163},"\"web.localhost:127.0.0.1\"\n",{"type":19,"tag":43,"props":3165,"children":3166},{"class":45,"line":245},[3167,3171],{"type":19,"tag":43,"props":3168,"children":3169},{"style":62},[3170],{"type":24,"value":3110},{"type":19,"tag":43,"props":3172,"children":3173},{"style":68},[3174],{"type":24,"value":3175},"\"api.localhost:127.0.0.1\"\n",{"type":19,"tag":2225,"props":3177,"children":3179},{"id":3178},"environment-variables",[3180],{"type":24,"value":3181},"Environment variables",{"type":19,"tag":32,"props":3183,"children":3185},{"className":2887,"code":3184,"language":2889,"meta":7,"style":7},"\u002F\u002F config.js\nconst isDev = process.env.NODE_ENV === 'development';\n\nexport const config = {\n  apiUrl: isDev ? 'https:\u002F\u002Fapi.localhost' : 'https:\u002F\u002Fapi.production.com',\n  authUrl: isDev ? 'https:\u002F\u002Fauth.localhost' : 'https:\u002F\u002Fauth.production.com',\n  webUrl: isDev ? 'https:\u002F\u002Fweb.localhost' : 'https:\u002F\u002Fapp.production.com'\n};\n",[3186],{"type":19,"tag":39,"props":3187,"children":3188},{"__ignoreMap":7},[3189,3197,3253,3260,3285,3326,3364,3398],{"type":19,"tag":43,"props":3190,"children":3191},{"class":45,"line":46},[3192],{"type":19,"tag":43,"props":3193,"children":3194},{"style":112},[3195],{"type":24,"value":3196},"\u002F\u002F config.js\n",{"type":19,"tag":43,"props":3198,"children":3199},{"class":45,"line":98},[3200,3205,3210,3215,3220,3224,3229,3233,3238,3243,3248],{"type":19,"tag":43,"props":3201,"children":3202},{"style":50},[3203],{"type":24,"value":3204},"const",{"type":19,"tag":43,"props":3206,"children":3207},{"style":450},[3208],{"type":24,"value":3209}," isDev",{"type":19,"tag":43,"props":3211,"children":3212},{"style":176},[3213],{"type":24,"value":3214}," =",{"type":19,"tag":43,"props":3216,"children":3217},{"style":450},[3218],{"type":24,"value":3219}," process",{"type":19,"tag":43,"props":3221,"children":3222},{"style":62},[3223],{"type":24,"value":1243},{"type":19,"tag":43,"props":3225,"children":3226},{"style":450},[3227],{"type":24,"value":3228},"env",{"type":19,"tag":43,"props":3230,"children":3231},{"style":62},[3232],{"type":24,"value":1243},{"type":19,"tag":43,"props":3234,"children":3235},{"style":56},[3236],{"type":24,"value":3237},"NODE_ENV",{"type":19,"tag":43,"props":3239,"children":3240},{"style":176},[3241],{"type":24,"value":3242}," ===",{"type":19,"tag":43,"props":3244,"children":3245},{"style":68},[3246],{"type":24,"value":3247}," 'development'",{"type":19,"tag":43,"props":3249,"children":3250},{"style":62},[3251],{"type":24,"value":3252},";\n",{"type":19,"tag":43,"props":3254,"children":3255},{"class":45,"line":108},[3256],{"type":19,"tag":43,"props":3257,"children":3258},{"emptyLinePlaceholder":102},[3259],{"type":24,"value":105},{"type":19,"tag":43,"props":3261,"children":3262},{"class":45,"line":118},[3263,3267,3272,3277,3281],{"type":19,"tag":43,"props":3264,"children":3265},{"style":50},[3266],{"type":24,"value":2909},{"type":19,"tag":43,"props":3268,"children":3269},{"style":50},[3270],{"type":24,"value":3271}," const",{"type":19,"tag":43,"props":3273,"children":3274},{"style":450},[3275],{"type":24,"value":3276}," config",{"type":19,"tag":43,"props":3278,"children":3279},{"style":176},[3280],{"type":24,"value":3214},{"type":19,"tag":43,"props":3282,"children":3283},{"style":62},[3284],{"type":24,"value":2919},{"type":19,"tag":43,"props":3286,"children":3287},{"class":45,"line":153},[3288,3293,3297,3302,3307,3312,3317,3322],{"type":19,"tag":43,"props":3289,"children":3290},{"style":56},[3291],{"type":24,"value":3292},"  apiUrl",{"type":19,"tag":43,"props":3294,"children":3295},{"style":62},[3296],{"type":24,"value":2945},{"type":19,"tag":43,"props":3298,"children":3299},{"style":56},[3300],{"type":24,"value":3301},"isDev",{"type":19,"tag":43,"props":3303,"children":3304},{"style":50},[3305],{"type":24,"value":3306}," ?",{"type":19,"tag":43,"props":3308,"children":3309},{"style":68},[3310],{"type":24,"value":3311}," 'https:\u002F\u002Fapi.localhost'",{"type":19,"tag":43,"props":3313,"children":3314},{"style":50},[3315],{"type":24,"value":3316}," :",{"type":19,"tag":43,"props":3318,"children":3319},{"style":68},[3320],{"type":24,"value":3321}," 'https:\u002F\u002Fapi.production.com'",{"type":19,"tag":43,"props":3323,"children":3324},{"style":62},[3325],{"type":24,"value":2955},{"type":19,"tag":43,"props":3327,"children":3328},{"class":45,"line":191},[3329,3334,3338,3342,3346,3351,3355,3360],{"type":19,"tag":43,"props":3330,"children":3331},{"style":56},[3332],{"type":24,"value":3333},"  authUrl",{"type":19,"tag":43,"props":3335,"children":3336},{"style":62},[3337],{"type":24,"value":2945},{"type":19,"tag":43,"props":3339,"children":3340},{"style":56},[3341],{"type":24,"value":3301},{"type":19,"tag":43,"props":3343,"children":3344},{"style":50},[3345],{"type":24,"value":3306},{"type":19,"tag":43,"props":3347,"children":3348},{"style":68},[3349],{"type":24,"value":3350}," 'https:\u002F\u002Fauth.localhost'",{"type":19,"tag":43,"props":3352,"children":3353},{"style":50},[3354],{"type":24,"value":3316},{"type":19,"tag":43,"props":3356,"children":3357},{"style":68},[3358],{"type":24,"value":3359}," 'https:\u002F\u002Fauth.production.com'",{"type":19,"tag":43,"props":3361,"children":3362},{"style":62},[3363],{"type":24,"value":2955},{"type":19,"tag":43,"props":3365,"children":3366},{"class":45,"line":200},[3367,3372,3376,3380,3384,3389,3393],{"type":19,"tag":43,"props":3368,"children":3369},{"style":56},[3370],{"type":24,"value":3371},"  webUrl",{"type":19,"tag":43,"props":3373,"children":3374},{"style":62},[3375],{"type":24,"value":2945},{"type":19,"tag":43,"props":3377,"children":3378},{"style":56},[3379],{"type":24,"value":3301},{"type":19,"tag":43,"props":3381,"children":3382},{"style":50},[3383],{"type":24,"value":3306},{"type":19,"tag":43,"props":3385,"children":3386},{"style":68},[3387],{"type":24,"value":3388}," 'https:\u002F\u002Fweb.localhost'",{"type":19,"tag":43,"props":3390,"children":3391},{"style":50},[3392],{"type":24,"value":3316},{"type":19,"tag":43,"props":3394,"children":3395},{"style":68},[3396],{"type":24,"value":3397}," 'https:\u002F\u002Fapp.production.com'\n",{"type":19,"tag":43,"props":3399,"children":3400},{"class":45,"line":209},[3401],{"type":19,"tag":43,"props":3402,"children":3403},{"style":62},[3404],{"type":24,"value":3405},"};\n",{"type":19,"tag":1099,"props":3407,"children":3409},{"id":3408},"common-gotchas",[3410],{"type":24,"value":3411},"Common gotchas",{"type":19,"tag":2225,"props":3413,"children":3415},{"id":3414},"browser-caching",[3416],{"type":24,"value":3417},"Browser caching",{"type":19,"tag":20,"props":3419,"children":3420},{},[3421],{"type":24,"value":3422},"Browsers sometimes cache DNS lookups. If subdomains aren't resolving:",{"type":19,"tag":32,"props":3424,"children":3426},{"className":2633,"code":3425,"language":2635,"meta":7,"style":7},"# macOS\nsudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder\n\n# Linux\nsudo systemctl restart systemd-resolved\n\n# Windows\nipconfig \u002Fflushdns\n",[3427],{"type":19,"tag":39,"props":3428,"children":3429},{"__ignoreMap":7},[3430,3438,3479,3486,3494,3516,3523,3531],{"type":19,"tag":43,"props":3431,"children":3432},{"class":45,"line":46},[3433],{"type":19,"tag":43,"props":3434,"children":3435},{"style":112},[3436],{"type":24,"value":3437},"# macOS\n",{"type":19,"tag":43,"props":3439,"children":3440},{"class":45,"line":98},[3441,3445,3450,3455,3460,3464,3469,3474],{"type":19,"tag":43,"props":3442,"children":3443},{"style":131},[3444],{"type":24,"value":2686},{"type":19,"tag":43,"props":3446,"children":3447},{"style":68},[3448],{"type":24,"value":3449}," dscacheutil",{"type":19,"tag":43,"props":3451,"children":3452},{"style":930},[3453],{"type":24,"value":3454}," -flushcache",{"type":19,"tag":43,"props":3456,"children":3457},{"style":62},[3458],{"type":24,"value":3459},"; ",{"type":19,"tag":43,"props":3461,"children":3462},{"style":131},[3463],{"type":24,"value":2686},{"type":19,"tag":43,"props":3465,"children":3466},{"style":68},[3467],{"type":24,"value":3468}," killall",{"type":19,"tag":43,"props":3470,"children":3471},{"style":930},[3472],{"type":24,"value":3473}," -HUP",{"type":19,"tag":43,"props":3475,"children":3476},{"style":68},[3477],{"type":24,"value":3478}," mDNSResponder\n",{"type":19,"tag":43,"props":3480,"children":3481},{"class":45,"line":108},[3482],{"type":19,"tag":43,"props":3483,"children":3484},{"emptyLinePlaceholder":102},[3485],{"type":24,"value":105},{"type":19,"tag":43,"props":3487,"children":3488},{"class":45,"line":118},[3489],{"type":19,"tag":43,"props":3490,"children":3491},{"style":112},[3492],{"type":24,"value":3493},"# Linux\n",{"type":19,"tag":43,"props":3495,"children":3496},{"class":45,"line":153},[3497,3501,3506,3511],{"type":19,"tag":43,"props":3498,"children":3499},{"style":131},[3500],{"type":24,"value":2686},{"type":19,"tag":43,"props":3502,"children":3503},{"style":68},[3504],{"type":24,"value":3505}," systemctl",{"type":19,"tag":43,"props":3507,"children":3508},{"style":68},[3509],{"type":24,"value":3510}," restart",{"type":19,"tag":43,"props":3512,"children":3513},{"style":68},[3514],{"type":24,"value":3515}," systemd-resolved\n",{"type":19,"tag":43,"props":3517,"children":3518},{"class":45,"line":191},[3519],{"type":19,"tag":43,"props":3520,"children":3521},{"emptyLinePlaceholder":102},[3522],{"type":24,"value":105},{"type":19,"tag":43,"props":3524,"children":3525},{"class":45,"line":200},[3526],{"type":19,"tag":43,"props":3527,"children":3528},{"style":112},[3529],{"type":24,"value":3530},"# Windows\n",{"type":19,"tag":43,"props":3532,"children":3533},{"class":45,"line":209},[3534,3539],{"type":19,"tag":43,"props":3535,"children":3536},{"style":131},[3537],{"type":24,"value":3538},"ipconfig",{"type":19,"tag":43,"props":3540,"children":3541},{"style":68},[3542],{"type":24,"value":3543}," \u002Fflushdns\n",{"type":19,"tag":2225,"props":3545,"children":3547},{"id":3546},"port-conflicts",[3548],{"type":24,"value":3549},"Port conflicts",{"type":19,"tag":32,"props":3551,"children":3553},{"className":2633,"code":3552,"language":2635,"meta":7,"style":7},"# Check what's running on port 3000\nlsof -i :3000\n",[3554],{"type":19,"tag":39,"props":3555,"children":3556},{"__ignoreMap":7},[3557,3565],{"type":19,"tag":43,"props":3558,"children":3559},{"class":45,"line":46},[3560],{"type":19,"tag":43,"props":3561,"children":3562},{"style":112},[3563],{"type":24,"value":3564},"# Check what's running on port 3000\n",{"type":19,"tag":43,"props":3566,"children":3567},{"class":45,"line":98},[3568,3573,3578],{"type":19,"tag":43,"props":3569,"children":3570},{"style":131},[3571],{"type":24,"value":3572},"lsof",{"type":19,"tag":43,"props":3574,"children":3575},{"style":930},[3576],{"type":24,"value":3577}," -i",{"type":19,"tag":43,"props":3579,"children":3580},{"style":68},[3581],{"type":24,"value":3582}," :3000\n",{"type":19,"tag":2225,"props":3584,"children":3586},{"id":3585},"ssl-certificate-issues",[3587],{"type":24,"value":3588},"SSL certificate issues",{"type":19,"tag":32,"props":3590,"children":3592},{"className":2633,"code":3591,"language":2635,"meta":7,"style":7},"# Reinstall mkcert CA\nmkcert -install\n\n# Regenerate certificates\nmkcert -key-file key.pem -cert-file cert.pem web.localhost api.localhost\n",[3593],{"type":19,"tag":39,"props":3594,"children":3595},{"__ignoreMap":7},[3596,3604,3615,3622,3630],{"type":19,"tag":43,"props":3597,"children":3598},{"class":45,"line":46},[3599],{"type":19,"tag":43,"props":3600,"children":3601},{"style":112},[3602],{"type":24,"value":3603},"# Reinstall mkcert CA\n",{"type":19,"tag":43,"props":3605,"children":3606},{"class":45,"line":98},[3607,3611],{"type":19,"tag":43,"props":3608,"children":3609},{"style":131},[3610],{"type":24,"value":2621},{"type":19,"tag":43,"props":3612,"children":3613},{"style":930},[3614],{"type":24,"value":2759},{"type":19,"tag":43,"props":3616,"children":3617},{"class":45,"line":108},[3618],{"type":19,"tag":43,"props":3619,"children":3620},{"emptyLinePlaceholder":102},[3621],{"type":24,"value":105},{"type":19,"tag":43,"props":3623,"children":3624},{"class":45,"line":118},[3625],{"type":19,"tag":43,"props":3626,"children":3627},{"style":112},[3628],{"type":24,"value":3629},"# Regenerate certificates\n",{"type":19,"tag":43,"props":3631,"children":3632},{"class":45,"line":153},[3633,3637,3642,3647,3652,3657,3661],{"type":19,"tag":43,"props":3634,"children":3635},{"style":131},[3636],{"type":24,"value":2621},{"type":19,"tag":43,"props":3638,"children":3639},{"style":930},[3640],{"type":24,"value":3641}," -key-file",{"type":19,"tag":43,"props":3643,"children":3644},{"style":68},[3645],{"type":24,"value":3646}," key.pem",{"type":19,"tag":43,"props":3648,"children":3649},{"style":930},[3650],{"type":24,"value":3651}," -cert-file",{"type":19,"tag":43,"props":3653,"children":3654},{"style":68},[3655],{"type":24,"value":3656}," cert.pem",{"type":19,"tag":43,"props":3658,"children":3659},{"style":68},[3660],{"type":24,"value":2786},{"type":19,"tag":43,"props":3662,"children":3663},{"style":68},[3664],{"type":24,"value":3665}," api.localhost\n",{"type":19,"tag":1099,"props":3667,"children":3669},{"id":3668},"final-thoughts",[3670],{"type":24,"value":3671},"Final thoughts",{"type":19,"tag":20,"props":3673,"children":3674},{},[3675,3680],{"type":19,"tag":39,"props":3676,"children":3678},{"className":3677},[],[3679],{"type":24,"value":2085},{"type":24,"value":3681}," is part of the spec for a reason. It gives you:",{"type":19,"tag":1566,"props":3683,"children":3684},{},[3685,3695,3705,3713,3723],{"type":19,"tag":1123,"props":3686,"children":3687},{},[3688,3693],{"type":19,"tag":1127,"props":3689,"children":3690},{},[3691],{"type":24,"value":3692},"Local-only domains",{"type":24,"value":3694}," that are DNS-safe and work on macOS, Linux, and Windows",{"type":19,"tag":1123,"props":3696,"children":3697},{},[3698,3703],{"type":19,"tag":1127,"props":3699,"children":3700},{},[3701],{"type":24,"value":3702},"Clean subdomain structure",{"type":24,"value":3704}," for microservices or frontends",{"type":19,"tag":1123,"props":3706,"children":3707},{},[3708],{"type":19,"tag":1127,"props":3709,"children":3710},{},[3711],{"type":24,"value":3712},"Accurate cookie, storage, and CORS behavior",{"type":19,"tag":1123,"props":3714,"children":3715},{},[3716,3721],{"type":19,"tag":1127,"props":3717,"children":3718},{},[3719],{"type":24,"value":3720},"Easy HTTPS",{"type":24,"value":3722}," with tools like mkcert",{"type":19,"tag":1123,"props":3724,"children":3725},{},[3726,3731],{"type":19,"tag":1127,"props":3727,"children":3728},{},[3729],{"type":24,"value":3730},"Less config and fewer bugs",{"type":24,"value":3732}," when you go live",{"type":19,"tag":1509,"props":3734,"children":3735},{},[3736],{"type":24,"value":1513},{"title":7,"searchDepth":98,"depth":98,"links":3738},[3739,3740,3741,3749,3750,3751,3756,3761],{"id":2090,"depth":98,"text":2093},{"id":2129,"depth":98,"text":2132},{"id":2204,"depth":98,"text":3742,"children":3743},"Cookie behavior on localhost",[3744,3745,3747],{"id":2227,"depth":108,"text":2230},{"id":2301,"depth":108,"text":3746},"127.0.0.1 and localhost are not treated the same",{"id":2350,"depth":108,"text":3748},"Using .localhost subdomains",{"id":2465,"depth":98,"text":2468},{"id":2595,"depth":98,"text":2598},{"id":2819,"depth":98,"text":2822,"children":3752},[3753,3754,3755],{"id":2881,"depth":108,"text":2884},{"id":3011,"depth":108,"text":3014},{"id":3178,"depth":108,"text":3181},{"id":3408,"depth":98,"text":3411,"children":3757},[3758,3759,3760],{"id":3414,"depth":108,"text":3417},{"id":3546,"depth":108,"text":3549},{"id":3585,"depth":108,"text":3588},{"id":3668,"depth":98,"text":3671},"content:blog:better-local-development-with-localhost-subdomains.md","blog\u002Fbetter-local-development-with-localhost-subdomains.md","blog\u002Fbetter-local-development-with-localhost-subdomains",[3766],{"_path":1527,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":1528,"description":1529,"date":1530,"topics":3767,"body":3768,"_type":1519,"_id":2050,"_source":1521,"_file":2051,"_stem":2052,"_extension":1524},[12,13,1532],{"type":16,"children":3769,"toc":4223},[3770,3780,3784,3788,3792,3807,3817,3821,3837,4071,4075,4102,4106,4110,4120,4215,4219],{"type":19,"tag":20,"props":3771,"children":3772},{},[3773,3774,3779],{"type":24,"value":1539},{"type":19,"tag":1222,"props":3775,"children":3777},{"href":1542,"rel":3776},[1226],[3778],{"type":24,"value":1546},{"type":24,"value":1548},{"type":19,"tag":1099,"props":3781,"children":3782},{"id":1551},[3783],{"type":24,"value":1554},{"type":19,"tag":20,"props":3785,"children":3786},{},[3787],{"type":24,"value":1559},{"type":19,"tag":20,"props":3789,"children":3790},{},[3791],{"type":24,"value":1564},{"type":19,"tag":1566,"props":3793,"children":3794},{},[3795,3799,3803],{"type":19,"tag":1123,"props":3796,"children":3797},{},[3798],{"type":24,"value":1573},{"type":19,"tag":1123,"props":3800,"children":3801},{},[3802],{"type":24,"value":1578},{"type":19,"tag":1123,"props":3804,"children":3805},{},[3806],{"type":24,"value":1583},{"type":19,"tag":20,"props":3808,"children":3809},{},[3810,3811,3816],{"type":24,"value":1588},{"type":19,"tag":1222,"props":3812,"children":3814},{"href":1591,"rel":3813},[1226],[3815],{"type":24,"value":1595},{"type":24,"value":1597},{"type":19,"tag":1099,"props":3818,"children":3819},{"id":1600},[3820],{"type":24,"value":1603},{"type":19,"tag":20,"props":3822,"children":3823},{},[3824,3825,3830,3831,3836],{"type":24,"value":1608},{"type":19,"tag":1222,"props":3826,"children":3828},{"href":1224,"rel":3827},[1226],[3829],{"type":24,"value":1229},{"type":24,"value":1615},{"type":19,"tag":39,"props":3832,"children":3834},{"className":3833},[],[3835],{"type":24,"value":1254},{"type":24,"value":457},{"type":19,"tag":32,"props":3838,"children":3839},{"className":34,"code":1624,"language":36,"meta":7,"style":7},[3840],{"type":19,"tag":39,"props":3841,"children":3842},{"__ignoreMap":7},[3843,3886,3925,3940,3967,3986,3993,4012,4019,4026,4053,4060],{"type":19,"tag":43,"props":3844,"children":3845},{"class":45,"line":46},[3846,3850,3854,3858,3862,3866,3870,3874,3878,3882],{"type":19,"tag":43,"props":3847,"children":3848},{"style":56},[3849],{"type":24,"value":406},{"type":19,"tag":43,"props":3851,"children":3852},{"style":62},[3853],{"type":24,"value":876},{"type":19,"tag":43,"props":3855,"children":3856},{"style":131},[3857],{"type":24,"value":881},{"type":19,"tag":43,"props":3859,"children":3860},{"style":62},[3861],{"type":24,"value":1322},{"type":19,"tag":43,"props":3863,"children":3864},{"style":68},[3865],{"type":24,"value":90},{"type":19,"tag":43,"props":3867,"children":3868},{"style":62},[3869],{"type":24,"value":1356},{"type":19,"tag":43,"props":3871,"children":3872},{"style":68},[3873],{"type":24,"value":1660},{"type":19,"tag":43,"props":3875,"children":3876},{"style":62},[3877],{"type":24,"value":76},{"type":19,"tag":43,"props":3879,"children":3880},{"style":50},[3881],{"type":24,"value":908},{"type":19,"tag":43,"props":3883,"children":3884},{"style":62},[3885],{"type":24,"value":913},{"type":19,"tag":43,"props":3887,"children":3888},{"class":45,"line":98},[3889,3893,3897,3901,3905,3909,3913,3917,3921],{"type":19,"tag":43,"props":3890,"children":3891},{"style":50},[3892],{"type":24,"value":391},{"type":19,"tag":43,"props":3894,"children":3895},{"style":56},[3896],{"type":24,"value":1684},{"type":19,"tag":43,"props":3898,"children":3899},{"style":62},[3900],{"type":24,"value":401},{"type":19,"tag":43,"props":3902,"children":3903},{"style":56},[3904],{"type":24,"value":406},{"type":19,"tag":43,"props":3906,"children":3907},{"style":62},[3908],{"type":24,"value":411},{"type":19,"tag":43,"props":3910,"children":3911},{"style":131},[3912],{"type":24,"value":1701},{"type":19,"tag":43,"props":3914,"children":3915},{"style":62},[3916],{"type":24,"value":139},{"type":19,"tag":43,"props":3918,"children":3919},{"style":68},[3920],{"type":24,"value":1710},{"type":19,"tag":43,"props":3922,"children":3923},{"style":62},[3924],{"type":24,"value":150},{"type":19,"tag":43,"props":3926,"children":3927},{"class":45,"line":108},[3928,3932,3936],{"type":19,"tag":43,"props":3929,"children":3930},{"style":50},[3931],{"type":24,"value":438},{"type":19,"tag":43,"props":3933,"children":3934},{"style":56},[3935],{"type":24,"value":1684},{"type":19,"tag":43,"props":3937,"children":3938},{"style":50},[3939],{"type":24,"value":1730},{"type":19,"tag":43,"props":3941,"children":3942},{"class":45,"line":118},[3943,3947,3951,3955,3959,3963],{"type":19,"tag":43,"props":3944,"children":3945},{"style":50},[3946],{"type":24,"value":1738},{"type":19,"tag":43,"props":3948,"children":3949},{"style":450},[3950],{"type":24,"value":1684},{"type":19,"tag":43,"props":3952,"children":3953},{"style":62},[3954],{"type":24,"value":457},{"type":19,"tag":43,"props":3956,"children":3957},{"style":131},[3958],{"type":24,"value":1751},{"type":19,"tag":43,"props":3960,"children":3961},{"style":62},[3962],{"type":24,"value":467},{"type":19,"tag":43,"props":3964,"children":3965},{"style":50},[3966],{"type":24,"value":506},{"type":19,"tag":43,"props":3968,"children":3969},{"class":45,"line":153},[3970,3974,3978,3982],{"type":19,"tag":43,"props":3971,"children":3972},{"style":450},[3973],{"type":24,"value":1767},{"type":19,"tag":43,"props":3975,"children":3976},{"style":62},[3977],{"type":24,"value":457},{"type":19,"tag":43,"props":3979,"children":3980},{"style":131},[3981],{"type":24,"value":1776},{"type":19,"tag":43,"props":3983,"children":3984},{"style":62},[3985],{"type":24,"value":913},{"type":19,"tag":43,"props":3987,"children":3988},{"class":45,"line":191},[3989],{"type":19,"tag":43,"props":3990,"children":3991},{"style":50},[3992],{"type":24,"value":1788},{"type":19,"tag":43,"props":3994,"children":3995},{"class":45,"line":200},[3996,4000,4004,4008],{"type":19,"tag":43,"props":3997,"children":3998},{"style":450},[3999],{"type":24,"value":1767},{"type":19,"tag":43,"props":4001,"children":4002},{"style":62},[4003],{"type":24,"value":457},{"type":19,"tag":43,"props":4005,"children":4006},{"style":131},[4007],{"type":24,"value":1804},{"type":19,"tag":43,"props":4009,"children":4010},{"style":62},[4011],{"type":24,"value":913},{"type":19,"tag":43,"props":4013,"children":4014},{"class":45,"line":209},[4015],{"type":19,"tag":43,"props":4016,"children":4017},{"style":50},[4018],{"type":24,"value":1816},{"type":19,"tag":43,"props":4020,"children":4021},{"class":45,"line":218},[4022],{"type":19,"tag":43,"props":4023,"children":4024},{"style":50},[4025],{"type":24,"value":1824},{"type":19,"tag":43,"props":4027,"children":4028},{"class":45,"line":227},[4029,4033,4037,4041,4045,4049],{"type":19,"tag":43,"props":4030,"children":4031},{"style":56},[4032],{"type":24,"value":515},{"type":19,"tag":43,"props":4034,"children":4035},{"style":62},[4036],{"type":24,"value":411},{"type":19,"tag":43,"props":4038,"children":4039},{"style":131},[4040],{"type":24,"value":1840},{"type":19,"tag":43,"props":4042,"children":4043},{"style":62},[4044],{"type":24,"value":139},{"type":19,"tag":43,"props":4046,"children":4047},{"style":68},[4048],{"type":24,"value":1710},{"type":19,"tag":43,"props":4050,"children":4051},{"style":62},[4052],{"type":24,"value":150},{"type":19,"tag":43,"props":4054,"children":4055},{"class":45,"line":236},[4056],{"type":19,"tag":43,"props":4057,"children":4058},{"style":50},[4059],{"type":24,"value":572},{"type":19,"tag":43,"props":4061,"children":4062},{"class":45,"line":245},[4063,4067],{"type":19,"tag":43,"props":4064,"children":4065},{"style":50},[4066],{"type":24,"value":946},{"type":19,"tag":43,"props":4068,"children":4069},{"style":62},[4070],{"type":24,"value":150},{"type":19,"tag":20,"props":4072,"children":4073},{},[4074],{"type":24,"value":1875},{"type":19,"tag":1119,"props":4076,"children":4077},{},[4078,4086,4094],{"type":19,"tag":1123,"props":4079,"children":4080},{},[4081,4085],{"type":19,"tag":1127,"props":4082,"children":4083},{},[4084],{"type":24,"value":1886},{"type":24,"value":1888},{"type":19,"tag":1123,"props":4087,"children":4088},{},[4089,4093],{"type":19,"tag":1127,"props":4090,"children":4091},{},[4092],{"type":24,"value":1896},{"type":24,"value":1898},{"type":19,"tag":1123,"props":4095,"children":4096},{},[4097,4101],{"type":19,"tag":1127,"props":4098,"children":4099},{},[4100],{"type":24,"value":1906},{"type":24,"value":1908},{"type":19,"tag":20,"props":4103,"children":4104},{},[4105],{"type":24,"value":1913},{"type":19,"tag":1099,"props":4107,"children":4108},{"id":1916},[4109],{"type":24,"value":1919},{"type":19,"tag":20,"props":4111,"children":4112},{},[4113,4114,4119],{"type":24,"value":1924},{"type":19,"tag":39,"props":4115,"children":4117},{"className":4116},[],[4118],{"type":24,"value":1710},{"type":24,"value":1931},{"type":19,"tag":32,"props":4121,"children":4122},{"className":34,"code":1934,"language":36,"meta":7,"style":7},[4123],{"type":19,"tag":39,"props":4124,"children":4125},{"__ignoreMap":7},[4126,4177,4204],{"type":19,"tag":43,"props":4127,"children":4128},{"class":45,"line":46},[4129,4133,4137,4141,4145,4149,4153,4157,4161,4165,4169,4173],{"type":19,"tag":43,"props":4130,"children":4131},{"style":56},[4132],{"type":24,"value":406},{"type":19,"tag":43,"props":4134,"children":4135},{"style":62},[4136],{"type":24,"value":1950},{"type":19,"tag":43,"props":4138,"children":4139},{"style":131},[4140],{"type":24,"value":1955},{"type":19,"tag":43,"props":4142,"children":4143},{"style":62},[4144],{"type":24,"value":139},{"type":19,"tag":43,"props":4146,"children":4147},{"style":56},[4148],{"type":24,"value":406},{"type":19,"tag":43,"props":4150,"children":4151},{"style":62},[4152],{"type":24,"value":411},{"type":19,"tag":43,"props":4154,"children":4155},{"style":131},[4156],{"type":24,"value":1972},{"type":19,"tag":43,"props":4158,"children":4159},{"style":62},[4160],{"type":24,"value":1977},{"type":19,"tag":43,"props":4162,"children":4163},{"style":50},[4164],{"type":24,"value":908},{"type":19,"tag":43,"props":4166,"children":4167},{"style":62},[4168],{"type":24,"value":139},{"type":19,"tag":43,"props":4170,"children":4171},{"style":142},[4172],{"type":24,"value":1990},{"type":19,"tag":43,"props":4174,"children":4175},{"style":62},[4176],{"type":24,"value":150},{"type":19,"tag":43,"props":4178,"children":4179},{"class":45,"line":98},[4180,4184,4188,4192,4196,4200],{"type":19,"tag":43,"props":4181,"children":4182},{"style":176},[4183],{"type":24,"value":2002},{"type":19,"tag":43,"props":4185,"children":4186},{"style":62},[4187],{"type":24,"value":139},{"type":19,"tag":43,"props":4189,"children":4190},{"style":450},[4191],{"type":24,"value":1990},{"type":19,"tag":43,"props":4193,"children":4194},{"style":62},[4195],{"type":24,"value":457},{"type":19,"tag":43,"props":4197,"children":4198},{"style":131},[4199],{"type":24,"value":2019},{"type":19,"tag":43,"props":4201,"children":4202},{"style":62},[4203],{"type":24,"value":2024},{"type":19,"tag":43,"props":4205,"children":4206},{"class":45,"line":108},[4207,4211],{"type":19,"tag":43,"props":4208,"children":4209},{"style":50},[4210],{"type":24,"value":946},{"type":19,"tag":43,"props":4212,"children":4213},{"style":62},[4214],{"type":24,"value":150},{"type":19,"tag":20,"props":4216,"children":4217},{},[4218],{"type":24,"value":2040},{"type":19,"tag":1509,"props":4220,"children":4221},{},[4222],{"type":24,"value":1513},{"title":7,"searchDepth":98,"depth":98,"links":4224},[4225,4226,4227],{"id":1551,"depth":98,"text":1554},{"id":1600,"depth":98,"text":1603},{"id":1916,"depth":98,"text":1919},1783615836868]