2025-01-05 04:56:57 +00:00
// Copyright 2025, Command Line Inc.
2024-08-01 05:22:52 +00:00
// SPDX-License-Identifier: Apache-2.0
2024-10-26 01:36:09 +00:00
import { BlockNodeModel } from "@/app/block/blocktypes" ;
2025-02-01 02:11:38 +00:00
import { getApi , globalStore , WOS } from "@/app/store/global" ;
import { RpcApi } from "@/app/store/wshclientapi" ;
import { TabRpcClient } from "@/app/store/wshrpcutil" ;
2024-10-08 00:20:18 +00:00
import { WebView , WebViewModel } from "@/app/view/webview/webview" ;
2024-10-09 19:42:33 +00:00
import { fireAndForget } from "@/util/util" ;
import { atom , useAtomValue } from "jotai" ;
2024-10-17 23:03:17 +00:00
import { useCallback } from "react" ;
2024-08-01 05:22:52 +00:00
2024-11-14 19:03:10 +00:00
const docsiteWebUrl = "https://docs.waveterm.dev/" ;
const baseUrlRegex = /http[s]?:\/\/([^:\/])+(:\d+)?/ ;
2024-10-08 00:20:18 +00:00
class HelpViewModel extends WebViewModel {
2025-02-12 01:20:45 +00:00
get viewComponent ( ) : ViewComponent {
return HelpView ;
}
2024-10-26 01:36:09 +00:00
constructor ( blockId : string , nodeModel : BlockNodeModel ) {
2024-10-08 00:20:18 +00:00
super ( blockId , nodeModel ) ;
2024-10-08 23:54:41 +00:00
this . viewText = atom ( ( get ) = > {
// force a dependency on meta.url so we re-render the buttons when the url changes
2024-11-14 19:03:10 +00:00
get ( this . blockAtom ) ? . meta ? . url || get ( this . homepageUrl ) ;
2024-10-08 23:54:41 +00:00
return [
{
elemtype : "iconbutton" ,
icon : "chevron-left" ,
click : this.handleBack.bind ( this ) ,
disabled : this.shouldDisableBackButton ( ) ,
} ,
{
elemtype : "iconbutton" ,
icon : "chevron-right" ,
click : this.handleForward.bind ( this ) ,
disabled : this.shouldDisableForwardButton ( ) ,
} ,
{
elemtype : "iconbutton" ,
icon : "house" ,
click : this.handleHome.bind ( this ) ,
disabled : this.shouldDisableHomeButton ( ) ,
} ,
] ;
} ) ;
2024-10-08 00:20:18 +00:00
this . homepageUrl = atom ( getApi ( ) . getDocsiteUrl ( ) ) ;
2024-09-05 04:15:39 +00:00
this . viewType = "help" ;
2024-10-08 00:20:18 +00:00
this . viewIcon = atom ( "circle-question" ) ;
this . viewName = atom ( "Help" ) ;
2024-11-14 19:03:10 +00:00
/ *
Add callback to take the current embedded docsite url and return the equivalent page in the public docsite .
The port used by the embedded docsite changes every time the app runs and the current page may be cached from a previous run so we can ' t trust that it matches the current embedded url .
We have a regex at the top of this file that can extract the base part of the url ( i . e . http : //127.0.0.1:53288). We'll use this regex to strip the base part of the url from both the current
page and the embedded docsite url . Because we host the embedded docsite at a subdirectory , we also need to strip that ( hence the second replace ) . Then , we can build the public url from whatever ' s left .
* /
this . modifyExternalUrl = ( url : string ) = > {
const strippedDocsiteUrl = getApi ( ) . getDocsiteUrl ( ) . replace ( baseUrlRegex , "" ) ;
const strippedCurUrl = url . replace ( baseUrlRegex , "" ) . replace ( strippedDocsiteUrl , "" ) ;
const newUrl = docsiteWebUrl + strippedCurUrl ;
2024-12-12 00:09:47 +00:00
console . log ( "modify-external-url" , url , newUrl ) ;
2024-11-14 19:03:10 +00:00
return newUrl ;
} ;
2024-09-05 04:15:39 +00:00
}
2024-11-26 02:07:29 +00:00
2025-02-01 02:11:38 +00:00
setZoomFactor ( factor : number | null ) {
// null is ok (will reset to default)
if ( factor != null && factor < 0.1 ) {
factor = 0.1 ;
}
if ( factor != null && factor > 5 ) {
factor = 5 ;
}
const domReady = globalStore . get ( this . domReady ) ;
if ( ! domReady ) {
return ;
}
this . webviewRef . current ? . setZoomFactor ( factor || 1 ) ;
RpcApi . SetMetaCommand ( TabRpcClient , {
oref : WOS.makeORef ( "block" , this . blockId ) ,
meta : { "web:zoom" : factor } , // allow null so we can remove the zoom factor here
} ) ;
}
2024-11-26 02:07:29 +00:00
getSettingsMenuItems ( ) : ContextMenuItem [ ] {
2025-02-01 02:11:38 +00:00
const zoomSubMenu : ContextMenuItem [ ] = [ ] ;
let curZoom = 1 ;
if ( globalStore . get ( this . domReady ) ) {
curZoom = this . webviewRef . current ? . getZoomFactor ( ) || 1 ;
}
const model = this ; // for the closure to work (this is getting unset)
function makeZoomFactorMenuItem ( label : string , factor : number ) : ContextMenuItem {
return {
label : label ,
type : "checkbox" ,
click : ( ) = > {
model . setZoomFactor ( factor ) ;
} ,
checked : curZoom == factor ,
} ;
}
zoomSubMenu . push ( {
label : "Reset" ,
click : ( ) = > {
model . setZoomFactor ( null ) ;
} ,
} ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "25%" , 0.25 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "50%" , 0.5 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "70%" , 0.7 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "80%" , 0.8 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "90%" , 0.9 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "100%" , 1 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "110%" , 1.1 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "120%" , 1.2 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "130%" , 1.3 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "150%" , 1.5 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "175%" , 1.75 ) ) ;
zoomSubMenu . push ( makeZoomFactorMenuItem ( "200%" , 2 ) ) ;
2024-11-26 02:07:29 +00:00
return [
{
label : this.webviewRef.current?.isDevToolsOpened ( ) ? "Close DevTools" : "Open DevTools" ,
click : async ( ) = > {
if ( this . webviewRef . current ) {
if ( this . webviewRef . current . isDevToolsOpened ( ) ) {
this . webviewRef . current . closeDevTools ( ) ;
} else {
this . webviewRef . current . openDevTools ( ) ;
}
}
} ,
} ,
2025-02-01 02:11:38 +00:00
{
label : "Set Zoom Factor" ,
submenu : zoomSubMenu ,
} ,
2024-11-26 02:07:29 +00:00
] ;
}
2024-09-05 04:15:39 +00:00
}
2025-02-11 00:26:12 +00:00
function HelpView ( props : ViewComponentProps < HelpViewModel > ) {
const model = props . model ;
2024-10-09 19:42:33 +00:00
const homepageUrl = useAtomValue ( model . homepageUrl ) ;
2024-10-17 22:38:42 +00:00
// Effect to update the docsite base url when the app restarts, since the webserver port is dynamic
2024-10-17 23:03:17 +00:00
const onFailLoad = useCallback (
( url : string ) = >
2024-10-09 19:42:33 +00:00
fireAndForget ( async ( ) = > {
2024-10-17 23:03:17 +00:00
const newDocsiteUrl = getApi ( ) . getDocsiteUrl ( ) ;
// Correct the homepage URL, if necessary
if ( newDocsiteUrl !== homepageUrl ) {
await model . setHomepageUrl ( newDocsiteUrl , "block" ) ;
}
// Correct the base URL of the current page, if necessary
const newBaseUrl = baseUrlRegex . exec ( newDocsiteUrl ) ? . [ 0 ] ;
const curBaseUrl = baseUrlRegex . exec ( url ) ? . [ 0 ] ;
if ( curBaseUrl && newBaseUrl && curBaseUrl !== newBaseUrl ) {
model . loadUrl ( url . replace ( curBaseUrl , newBaseUrl ) , "fix-fail-load" ) ;
2024-10-09 19:42:33 +00:00
}
} ) ,
2024-10-17 23:03:17 +00:00
[ homepageUrl ]
2024-10-09 19:42:33 +00:00
) ;
2024-10-04 03:28:05 +00:00
return (
2025-08-22 21:29:30 +00:00
< div className = "w-full h-full" >
2025-02-11 00:26:12 +00:00
< WebView { ...props } onFailLoad = { onFailLoad } / >
2024-10-04 03:28:05 +00:00
< / div >
) ;
2024-08-01 05:22:52 +00:00
}
2025-02-12 01:20:45 +00:00
export { HelpViewModel } ;