Skip to content

Commit

Permalink
feat(browser): add omitbox
Browse files Browse the repository at this point in the history
  • Loading branch information
CyanSalt committed Oct 10, 2024
1 parent ff8d748 commit 6479753
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 76 deletions.
132 changes: 114 additions & 18 deletions addons/browser/src/renderer/BrowserPane.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<script lang="ts" setup>
import type { TerminalTab } from '@commas/types/terminal'
import * as commas from 'commas:api/renderer'
import { watchEffect } from 'vue'
import { nextTick, watchEffect } from 'vue'
const { tab } = defineProps<{
tab: TerminalTab,
}>()
const { TerminalBlock, WebContents } = commas.ui.vueAssets
const { TerminalPane, WebContents, VisualIcon } = commas.ui.vueAssets
let url = $computed({
get: () => tab.command,
Expand All @@ -18,32 +18,128 @@ let url = $computed({
},
})
let title = $ref<string>()
let icon = $ref<string>()
let view = $ref<commas.ui.RendererWebContentsView>()
function initialize(current: commas.ui.RendererWebContentsView) {
view = current
}
watchEffect(onInvalidate => {
// eslint-disable-next-line vue/no-mutating-props
tab.title = title
onInvalidate(() => {
if (view) {
// eslint-disable-next-line vue/no-mutating-props
delete tab.title
})
tab.title = view.title || ''
onInvalidate(() => {
// eslint-disable-next-line vue/no-mutating-props
tab.title = ''
})
}
})
function goBack() {
if (!view) return
view.goToOffset(-1)
}
let isCustomizing = $ref(false)
let customURL: string | undefined = $ref<string>()
let customURLElement = $ref<HTMLInputElement>()
watchEffect(() => {
customURL = url
})
async function startCustomization() {
isCustomizing = true
await nextTick()
if (customURLElement) {
customURLElement.select()
}
}
async function customize() {
if (customURL !== url) {
url = customURL
}
isCustomizing = false
}
function resetCustomization() {
customURL = url
isCustomizing = false
}
function autoselect(event: FocusEvent) {
(event.target as HTMLInputElement).select()
}
</script>

<template>
<TerminalBlock :tab="tab" class="browser-pane">
<WebContents
v-model="url"
v-model:title="title"
v-model:icon="icon"
class="web-page"
/>
</TerminalBlock>
<TerminalPane :tab="tab" class="browser-pane">
<div class="browser-view">
<div class="form-line action-line">
<span :class="['link', 'form-action', { disabled: !view?.canGoBack }]" @click="goBack">
<VisualIcon name="lucide-undo-2" />
</span>
<form v-if="isCustomizing" class="custom-url-form" @submit.prevent="customize">
<input
ref="customURLElement"
v-model="customURL"
class="custom-url"
autofocus
@focus="autoselect"
@blur="resetCustomization"
@keydown.esc="resetCustomization"
>
</form>
<span v-else class="page-url" @click="startCustomization">{{ url }}</span>
</div>
<WebContents
v-model="url"
class="web-page"
@initialize="initialize"
/>
</div>
</TerminalPane>
</template>

<style lang="scss" scoped>
.web-page {
.browser-view {
display: flex;
flex-direction: column;
height: 100%;
}
.browser-pane {
padding: 0;
overflow: visible;
}
.action-line {
flex: none;
gap: 4px;
padding: 8px;
}
.custom-url-form {
display: flex;
flex: 1;
min-width: 0;
}
.custom-url {
flex: 1;
min-width: 0;
padding: 0;
border: none;
color: inherit;
font-family: inherit;
font-size: 12px;
background: transparent;
outline: none;
}
.page-url {
flex: 1;
min-width: 0;
font-size: 12px;
}
.web-page {
flex: 1;
min-height: 0;
}
</style>
2 changes: 2 additions & 0 deletions src/api/modules/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ObjectEditor from '../../renderer/components/basic/ObjectEditor.vue'
import SwitchControl from '../../renderer/components/basic/SwitchControl.vue'
import ValueSelector from '../../renderer/components/basic/ValueSelector.vue'
import VisualIcon from '../../renderer/components/basic/VisualIcon.vue'
import { RendererWebContentsView } from '../../renderer/compositions/web-contents'
import { createContextMenu, openContextMenu, withContextMenuSeparator } from '../../renderer/utils/frame'
import { vI18n } from '../../renderer/utils/i18n'
import type { RendererAPIContext } from '../types'
Expand Down Expand Up @@ -58,4 +59,5 @@ export {
createContextMenu,
withContextMenuSeparator,
extractClosestEdge,
RendererWebContentsView,
}
4 changes: 2 additions & 2 deletions src/main/lib/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ declare module '@commas/electron-ipc' {
export interface RendererEvents {
'view-title-updated': (id: number, title: string) => void,
'view-icon-updated': (id: number, icon: string | undefined) => void,
'view-url-updated': (id: number, url: string) => void,
'view-url-updated': (id: number, url: string, canGoBack: boolean) => void,
'view-open-url': (url: string) => void,
}
}
Expand Down Expand Up @@ -285,7 +285,7 @@ function handleViewEvents(view: WebContentsView, parent: WebContents) {
send(parent, 'view-icon-updated', view.webContents.id, icons[icons.length - 1])
})
view.webContents.on('did-start-navigation', (details) => {
send(parent, 'view-url-updated', view.webContents.id, details.url)
send(parent, 'view-url-updated', view.webContents.id, details.url, view.webContents.navigationHistory.canGoBack())
})
}

Expand Down
6 changes: 6 additions & 0 deletions src/main/lib/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ declare module '@commas/electron-ipc' {
'destroy-web-contents': (id: number) => void,
'navigate-web-contents': (id: number, url: string) => void,
'resize-web-contents': (id: number, rect: Rectangle) => void,
'go-to-offset-web-contents': (id: number, offset: number) => void,
}
}

Expand Down Expand Up @@ -170,6 +171,11 @@ function handleWindowMessages() {
if (!view) return
view.setBounds(rect)
})
ipcMain.handle('go-to-offset-web-contents', (event, id, offset) => {
const view = Array.from(webContentsViews).find(item => item.webContents.id === id)
if (!view) return
view.webContents.navigationHistory.goToOffset(offset)
})
}

export {
Expand Down
25 changes: 10 additions & 15 deletions src/renderer/components/WebContents.vue
Original file line number Diff line number Diff line change
@@ -1,36 +1,31 @@
<script lang="ts" setup>
import { useResizeObserver } from '@vueuse/core'
import { onUnmounted, watch, watchEffect } from 'vue'
import { ipcRenderer } from '@commas/electron-ipc'
import { createWebContents, navigateWebContents, RendererWebContentsView, resizeWebContents } from '../compositions/web-contents'
import { RendererWebContentsView } from '../compositions/web-contents'
let url = $(defineModel<string>())
let title = $(defineModel<string>('title'))
let icon = $(defineModel<string>('icon'))
const emit = defineEmits<{
(event: 'initialize', view: RendererWebContentsView): void,
}>()
let root = $ref<HTMLDivElement>()
let view = $ref<RendererWebContentsView>()
watch($$(root), async element => {
if (!element) return
view = await createWebContents(element)
view = await RendererWebContentsView.create(element)
emit('initialize', view)
}, { immediate: true })
onUnmounted(async () => {
if (!view) return
ipcRenderer.invoke('destroy-web-contents', view.id)
view.destroy()
})
useResizeObserver($$(root), async () => {
if (!view) return
resizeWebContents(view.id, root!)
})
watchEffect(() => {
title = view?.title
})
watchEffect(() => {
icon = view?.icon
view.resize(root!)
})
watch(() => view?.url, value => {
Expand All @@ -42,7 +37,7 @@ watch(() => view?.url, value => {
watchEffect(() => {
if (!url || !view) return
if (url !== view.url) {
navigateWebContents(view.id, url)
view.navigate(url)
}
})
</script>
Expand Down
91 changes: 50 additions & 41 deletions src/renderer/compositions/web-contents.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,64 @@
import { ipcRenderer } from '@commas/electron-ipc'
import { globalHandler } from '../../shared/handler'

export interface RendererWebContentsView {
id: number,
url?: string,
title?: string,
icon?: string,
loading?: boolean,
}

const webContentsViews = $ref<RendererWebContentsView[]>([])
export function useWebContentsViews() {
return $$(webContentsViews)
}

export async function createWebContents(element: HTMLElement) {
const rect = element.getBoundingClientRect()
const id = await ipcRenderer.invoke('create-web-contents', {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
})
const view: RendererWebContentsView = { id }
webContentsViews.push(view)
return view
}
export class RendererWebContentsView {

id: number
url?: string
title?: string
icon?: string
canGoBack?: boolean

export function destroyWebContents(id: number) {
const index = webContentsViews.findIndex(item => item.id === id)
if (index !== -1) {
webContentsViews.splice(index, 1)
constructor(id: number) {
this.id = id
}
return ipcRenderer.invoke('destroy-web-contents', id)
}

export function navigateWebContents(id: number, url: string) {
const view = webContentsViews.find(item => item.id === id)
if (view) {
view.url = url
static async create(element: HTMLElement) {
const rect = element.getBoundingClientRect()
const id = await ipcRenderer.invoke('create-web-contents', {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
})
const view = new RendererWebContentsView(id)
webContentsViews.push(view)
return view
}

async navigate(url: string) {
if (url === this.url) return
this.url = url
return ipcRenderer.invoke('navigate-web-contents', this.id, url)
}

resize(element: HTMLElement) {
const rect = element.getBoundingClientRect()
return ipcRenderer.invoke('resize-web-contents', this.id, {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
})
}

goToOffset(offset: number) {
return ipcRenderer.invoke('go-to-offset-web-contents', this.id, offset)
}

destroy() {
const index = webContentsViews.indexOf(this)
if (index !== -1) {
webContentsViews.splice(index, 1)
}
return ipcRenderer.invoke('destroy-web-contents', this.id)
}
return ipcRenderer.invoke('navigate-web-contents', id, url)
}

export function resizeWebContents(id: number, element: HTMLElement) {
const rect = element.getBoundingClientRect()
return ipcRenderer.invoke('resize-web-contents', id, {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
})
}

export function handleWebContentsMessages() {
Expand All @@ -64,10 +72,11 @@ export function handleWebContentsMessages() {
if (!view) return
view.icon = icon
})
ipcRenderer.on('view-url-updated', (event, id, url) => {
ipcRenderer.on('view-url-updated', (event, id, url, canGoBack) => {
const view = webContentsViews.find(item => item.id === id)
if (!view) return
view.url = url
view.canGoBack = canGoBack
})
ipcRenderer.on('view-open-url', (event, url) => {
globalHandler.invoke('global-renderer:open-url', url)
Expand Down

0 comments on commit 6479753

Please sign in to comment.