在终端应用程序 Hyper 中获取并排预览

2025-06-08

在终端应用程序 Hyper 中获取并排预览

大家好,我是Takuy​​a
我正在发布YouTube 内容,分享我的开发工作流程。

想要在终端应用中并排预览

对于网页编程教程来说,并排展示代码和输出效果会很棒。
通常情况下,只需排列两个窗口即可:一个编辑器和一个浏览器。
但这很烦人,因为每次制作教程时,你都必须并排对齐这两个窗口。

Hyper是一款使用 Web 标准构建的终端应用程序,曾经具有内置的 WebView 功能:

Hyper 内置的 webview 功能

如上所示,它在终端窗口右侧显示了预览。
看起来非常简洁。
因此,您无需单独的预览窗口即可制作教程。

但看起来该功能已因安全原因被删除:

那是三年前的事了,一个相当老的问题。
我理解在 Electron 应用中加载网页的风险。
然而,webview最近Electron 默认禁止集成 NodeJS。所以,我认为嵌入 Hyper 是安全的,尤其是在你了解自己在做什么的情况下。

破解 Hyper 以恢复内置的 webview 功能

因此,我决定将这个功能重新引入 Hyper,并成功实现了。
它的样子如下:

演示

演示视频:

如何构建

我已经built-in-webview为 hack 创建了分支:

https://github.com/craftzdog/hyper/tree/built-in-webview

克隆此 repo 并按照如下方式自行构建。

npm i
npm run dev
# On another terminal session
npm run app
Enter fullscreen mode Exit fullscreen mode

如何使用

按 向右拆分窗口Cmd-D
然后,输入echo <URL-to-open>并点击Return
在终端中点击 URL。
然后,窗格将变成加载 URL 的 Web 视图。

我如何破解 Hyper

检查差异

首先,您必须允许webview标签app/ui/window.ts

   const winOpts: BrowserWindowConstructorOptions = {
     minWidth: 370,
     minHeight: 190,
     backgroundColor: toElectronBackgroundColor(cfg.backgroundColor || '#000'),
     titleBarStyle: 'hiddenInset',
     title: 'Hyper.app',
     // we want to go frameless on Windows and Linux
     frame: process.platform === 'darwin',
     transparent: process.platform === 'darwin',
     icon,
     show: Boolean(process.env.HYPER_DEBUG || process.env.HYPERTERM_DEBUG || isDev),
     acceptFirstMouse: true,
     webPreferences: {
       nodeIntegration: true,
       navigateOnDragDrop: true,
       enableRemoteModule: true,
-      contextIsolation: false
+      contextIsolation: false,
+      webviewTag: true
     },
     ...options_
   };
Enter fullscreen mode Exit fullscreen mode

Hyper 部分保留了旧实现,您可以复用它们。
终端组件已经拥有urlprop,
您可以webview在组件拥有urlprop 时进行显示。

在中lib/components/term.tsx,像这样更改终端组件类:

@@ -430,18 +436,35 @@ export default class Term extends React.PureComponent<TermProps> {
         style={{padding: this.props.padding}}
         onMouseUp={this.onMouseUp}
       >
-        {this.props.customChildrenBefore}
-        <div ref={this.onTermWrapperRef} className="term_fit term_wrapper" />
-        {this.props.customChildren}
-        {this.props.search ? (
-          <SearchBox
-            search={this.search}
-            next={this.searchNext}
-            prev={this.searchPrevious}
-            close={this.closeSearchBox}
+        {this.props.url ? (
+          <webview
+            src={this.props.url}
+            style={{
+              background: '#fff',
+              position: 'absolute',
+              top: 0,
+              left: 0,
+              display: 'inline-flex',
+              width: '100%',
+              height: '100%'
+            }}
           />
         ) : (
-          ''
+          <>
+            {this.props.customChildrenBefore}
+            <div ref={this.onTermWrapperRef} className="term_fit term_wrapper" />
+            {this.props.customChildren}
+            {this.props.search ? (
+              <SearchBox
+                search={this.search}
+                next={this.searchNext}
+                prev={this.searchPrevious}
+                close={this.closeSearchBox}
+              />
+            ) : (
+              ''
+            )}
+          </>
         )}

         <style jsx global>{`
Enter fullscreen mode Exit fullscreen mode

并且,更改 URL 点击处理程序以分派操作,而不是在外部浏览器中打开页面:

@@ -160,7 +160,13 @@ export default class Term extends React.PureComponent<TermProps> {
       this.term.loadAddon(
         new WebLinksAddon(
           (event: MouseEvent | undefined, uri: string) => {
-            if (shallActivateWebLink(event)) void shell.openExternal(uri);
+            // if (shallActivateWebLink(event)) void shell.openExternal(uri);
+            // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+            store.dispatch({
+              type: 'SESSION_URL_SET',
+              uid: props.uid,
+              url: uri
+            });
           },
           {
             // prevent default electron link handling to allow selection, e.g. via double-click
Enter fullscreen mode Exit fullscreen mode

在 中lib/reducers/sessions.ts,添加 reducer,SESSION_URL_SET如下所示:

@@ -11,7 +11,8 @@ import {
   SESSION_SET_XTERM_TITLE,
   SESSION_SET_CWD,
   SESSION_SEARCH,
-  SESSION_SEARCH_CLOSE
+  SESSION_SEARCH_CLOSE,
+  SESSION_URL_SET
 } from '../constants/sessions';
 import {sessionState, session, Mutable, ISessionReducer} from '../hyper';

@@ -129,6 +130,9 @@ const reducer: ISessionReducer = (state = initialState, action) => {
       }
       return state;

+    case SESSION_URL_SET:
+      return state.setIn(['sessions', action.uid, 'url'], action.url);
+
     default:
       return state;
   }
Enter fullscreen mode Exit fullscreen mode

它非常有效!

在线关注我

鏂囩珷鏉ユ簮锛�https://dev.to/craftzdog/getting-side-by-side-preview-in-a-terminal-app-hyper-20ii
PREV
提升开发者技能的 4 个简单方法
NEXT
2024 年面向开发人员的 10 款无代码工具