@@ -19,18 +19,19 @@ import type { MenuItemConstructorOptions } from "electron";
1919import * as Effect from "effect/Effect" ;
2020import type {
2121 DesktopPreviewBounds ,
22- DesktopPreviewState ,
2322 DesktopTheme ,
2423 DesktopUpdateActionResult ,
2524 DesktopUpdateState ,
25+ PreviewTabId ,
26+ PreviewTabsState ,
2627} from "@okcode/contracts" ;
2728import { autoUpdater } from "electron-updater" ;
2829
2930import type { ContextMenuItem } from "@okcode/contracts" ;
3031import { NetService } from "@okcode/shared/Net" ;
3132import { RotatingFileSink } from "@okcode/shared/logging" ;
3233import { showDesktopConfirmDialog } from "./confirmDialog" ;
33- import { createClosedPreviewState } from "./preview" ;
34+ import { createEmptyTabsState } from "./preview" ;
3435import { DesktopPreviewController } from "./previewController" ;
3536import { syncShellEnvironment } from "./syncShellEnvironment" ;
3637import { getAutoUpdateDisabledReason , shouldBroadcastDownloadProgress } from "./updateState" ;
@@ -62,15 +63,18 @@ const UPDATE_STATE_CHANNEL = "desktop:update-state";
6263const UPDATE_GET_STATE_CHANNEL = "desktop:update-get-state" ;
6364const UPDATE_DOWNLOAD_CHANNEL = "desktop:update-download" ;
6465const UPDATE_INSTALL_CHANNEL = "desktop:update-install" ;
65- const PREVIEW_OPEN_CHANNEL = "desktop:preview-open" ;
66- const PREVIEW_CLOSE_CHANNEL = "desktop:preview-close" ;
66+ const PREVIEW_CREATE_TAB_CHANNEL = "desktop:preview-create-tab" ;
67+ const PREVIEW_CLOSE_TAB_CHANNEL = "desktop:preview-close-tab" ;
68+ const PREVIEW_ACTIVATE_TAB_CHANNEL = "desktop:preview-activate-tab" ;
6769const PREVIEW_GO_BACK_CHANNEL = "desktop:preview-go-back" ;
6870const PREVIEW_GO_FORWARD_CHANNEL = "desktop:preview-go-forward" ;
6971const PREVIEW_RELOAD_CHANNEL = "desktop:preview-reload" ;
7072const PREVIEW_NAVIGATE_CHANNEL = "desktop:preview-navigate" ;
73+ const PREVIEW_TOGGLE_DEVTOOLS_CHANNEL = "desktop:preview-toggle-devtools" ;
7174const PREVIEW_GET_STATE_CHANNEL = "desktop:preview-get-state" ;
7275const PREVIEW_SET_BOUNDS_CHANNEL = "desktop:preview-set-bounds" ;
73- const PREVIEW_STATE_CHANNEL = "desktop:preview-state" ;
76+ const PREVIEW_CLOSE_ALL_CHANNEL = "desktop:preview-close-all" ;
77+ const PREVIEW_TABS_STATE_CHANNEL = "desktop:preview-tabs-state" ;
7478const BASE_DIR = process . env . OKCODE_HOME ?. trim ( ) || Path . join ( OS . homedir ( ) , ".okcode" ) ;
7579const STATE_DIR = Path . join ( BASE_DIR , "userdata" ) ;
7680const DESKTOP_SCHEME = "okcode" ;
@@ -749,11 +753,11 @@ function emitUpdateState(): void {
749753 }
750754}
751755
752- function emitPreviewState ( window : BrowserWindow , state : DesktopPreviewState ) : void {
756+ function emitPreviewState ( window : BrowserWindow , state : PreviewTabsState ) : void {
753757 if ( window . isDestroyed ( ) ) {
754758 return ;
755759 }
756- window . webContents . send ( PREVIEW_STATE_CHANNEL , state ) ;
760+ window . webContents . send ( PREVIEW_TABS_STATE_CHANNEL , state ) ;
757761}
758762
759763function getPreviewController ( window : BrowserWindow ) : DesktopPreviewController {
@@ -1279,27 +1283,33 @@ function registerIpcHandlers(): void {
12791283 } satisfies DesktopUpdateActionResult ;
12801284 } ) ;
12811285
1282- ipcMain . removeHandler ( PREVIEW_OPEN_CHANNEL ) ;
1283- ipcMain . handle ( PREVIEW_OPEN_CHANNEL , async ( event , input : { url ?: unknown ; title ?: unknown } ) => {
1286+ ipcMain . removeHandler ( PREVIEW_CREATE_TAB_CHANNEL ) ;
1287+ ipcMain . handle (
1288+ PREVIEW_CREATE_TAB_CHANNEL ,
1289+ async ( event , input : { url ?: unknown ; title ?: unknown } ) => {
1290+ const window = resolvePreviewWindow ( event . sender ) ;
1291+ if ( ! window ) {
1292+ return { tabId : "" , state : createEmptyTabsState ( ) } ;
1293+ }
1294+ return getPreviewController ( window ) . createTab ( {
1295+ url : input ?. url ,
1296+ title : input ?. title ,
1297+ } ) ;
1298+ } ,
1299+ ) ;
1300+
1301+ ipcMain . removeHandler ( PREVIEW_CLOSE_TAB_CHANNEL ) ;
1302+ ipcMain . handle ( PREVIEW_CLOSE_TAB_CHANNEL , async ( event , input : { tabId ?: PreviewTabId } ) => {
12841303 const window = resolvePreviewWindow ( event . sender ) ;
1285- if ( ! window ) {
1286- return {
1287- accepted : false ,
1288- state : getPreviewController ( mainWindow ?? createWindow ( ) ) . getState ( ) ,
1289- } ;
1290- }
1291- const controller = getPreviewController ( window ) ;
1292- return controller . open ( {
1293- url : input ?. url ,
1294- title : input ?. title ,
1295- } ) ;
1304+ if ( ! window || ! input ?. tabId ) return createEmptyTabsState ( ) ;
1305+ return getPreviewController ( window ) . closeTab ( input . tabId ) ;
12961306 } ) ;
12971307
1298- ipcMain . removeHandler ( PREVIEW_CLOSE_CHANNEL ) ;
1299- ipcMain . handle ( PREVIEW_CLOSE_CHANNEL , async ( event ) => {
1308+ ipcMain . removeHandler ( PREVIEW_ACTIVATE_TAB_CHANNEL ) ;
1309+ ipcMain . handle ( PREVIEW_ACTIVATE_TAB_CHANNEL , async ( event , input : { tabId ?: PreviewTabId } ) => {
13001310 const window = resolvePreviewWindow ( event . sender ) ;
1301- if ( ! window ) return ;
1302- getPreviewController ( window ) . close ( ) ;
1311+ if ( ! window || ! input ?. tabId ) return createEmptyTabsState ( ) ;
1312+ return getPreviewController ( window ) . activateTab ( input . tabId ) ;
13031313 } ) ;
13041314
13051315 ipcMain . removeHandler ( PREVIEW_GO_BACK_CHANNEL ) ;
@@ -1327,21 +1337,23 @@ function registerIpcHandlers(): void {
13271337 ipcMain . handle ( PREVIEW_NAVIGATE_CHANNEL , async ( event , input : { url ?: unknown } ) => {
13281338 const window = resolvePreviewWindow ( event . sender ) ;
13291339 if ( ! window ) {
1330- return {
1331- accepted : false ,
1332- state : getPreviewController ( mainWindow ?? createWindow ( ) ) . getState ( ) ,
1333- } ;
1340+ return { accepted : false , state : createEmptyTabsState ( ) } ;
13341341 }
1335- return getPreviewController ( window ) . navigate ( {
1336- url : input ?. url ,
1337- } ) ;
1342+ return getPreviewController ( window ) . navigate ( { url : input ?. url } ) ;
1343+ } ) ;
1344+
1345+ ipcMain . removeHandler ( PREVIEW_TOGGLE_DEVTOOLS_CHANNEL ) ;
1346+ ipcMain . handle ( PREVIEW_TOGGLE_DEVTOOLS_CHANNEL , async ( event ) => {
1347+ const window = resolvePreviewWindow ( event . sender ) ;
1348+ if ( ! window ) return ;
1349+ getPreviewController ( window ) . toggleDevTools ( ) ;
13381350 } ) ;
13391351
13401352 ipcMain . removeHandler ( PREVIEW_GET_STATE_CHANNEL ) ;
13411353 ipcMain . handle ( PREVIEW_GET_STATE_CHANNEL , async ( event ) => {
13421354 const window = resolvePreviewWindow ( event . sender ) ;
13431355 if ( ! window ) {
1344- return createClosedPreviewState ( ) ;
1356+ return createEmptyTabsState ( ) ;
13451357 }
13461358 return getPreviewController ( window ) . getState ( ) ;
13471359 } ) ;
@@ -1352,6 +1364,13 @@ function registerIpcHandlers(): void {
13521364 if ( ! window ) return ;
13531365 getPreviewController ( window ) . setBounds ( bounds ) ;
13541366 } ) ;
1367+
1368+ ipcMain . removeHandler ( PREVIEW_CLOSE_ALL_CHANNEL ) ;
1369+ ipcMain . handle ( PREVIEW_CLOSE_ALL_CHANNEL , async ( event ) => {
1370+ const window = resolvePreviewWindow ( event . sender ) ;
1371+ if ( ! window ) return ;
1372+ getPreviewController ( window ) . closeAll ( ) ;
1373+ } ) ;
13551374}
13561375
13571376function getIconOption ( ) : { icon : string } | Record < string , never > {
0 commit comments