feat: 新增包文件

This commit is contained in:
duanshuwen
2025-11-15 22:41:50 +08:00
parent 7b65193e5c
commit 7ada85f175
104 changed files with 11273 additions and 1 deletions

View File

@@ -0,0 +1,9 @@
{
"name": "WebUI",
"version": "1.0.0",
"manifest_version": 3,
"permissions": [],
"chrome_url_overrides": {
"newtab": "new-tab.html"
}
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>New Tab</title>
<style>
body {
font-size: 16px;
}
</style>
</head>
<body>
<h1>New Tab</h1>
<ul>
<li><a href="https://www.google.com">https://www.google.com</a></li>
<li><a href="https://www.youtube.com">https://www.youtube.com</a></li>
<li>
<a href="https://github.com/electron/electron">https://github.com/electron/electron</a>
</li>
<li>
<a href="https://github.com/samuelmaddock/electron-browser-shell"
>https://github.com/samuelmaddock/electron-browser-shell</a
>
</li>
<li><a href="https://chromewebstore.google.com">https://chromewebstore.google.com</a></li>
<li><a href="https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home">https://microsoftedge.microsoft.com/addons/Microsoft-Edge-Extensions-Home</a></li>
<li><a href="https://permission.site">https://permission.site</a></li>
<li><a href="https://samuelmaddock.com">https://samuelmaddock.com</a></li>
</ul>
</body>
</html>

View File

@@ -0,0 +1,253 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Shell Browser</title>
<style>
:root {
/* https://colorhunt.co/palette/161920 */
--bg-color: #39375b;
--text-color: #ececec;
--tab-color: #745c97;
--toolbar-control-color: #d597ce;
/* electron-chrome-extensions colors */
--browser-action-badge-outline: var(--tab-color);
}
*, *:before, *:after {
box-sizing: border-box;
user-select: none;
}
html {
background: var(--tab-color);
height: 100%;
margin: 0;
padding: 0;
}
body {
padding: 0;
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe WPC", "Segoe UI", "Ubuntu", "Droid Sans", sans-serif;
font-size: 16px;
color: var(--text-color);
}
ul {
margin: 0;
padding: 0;
list-style: none;
}
.topbar {
background: var(--bg-color);
}
/*----- TABS -----*/
#tabstrip {
width: 100%;
height: calc(env(titlebar-area-height, 31px) + 1px);
}
.tab-container {
position: fixed;
left: env(titlebar-area-x);
top: env(titlebar-area-y);
width: env(titlebar-area-width);
height: calc(env(titlebar-area-height, 31px) + 1px);
display: flex;
flex-direction: row;
}
.tab-list {
height: 100%;
display: flex;
flex-direction: row;
min-width: 0;
}
#createtab {
background: transparent;
border: none;
color: #fff;
font-weight: bold;
font-family: 'Courier New', Courier, monospace;
}
#createtab:hover {
background: rgba(255,255,255,0.2);
}
.tab {
padding: 0.2rem 0.4rem;
height: 100%;
overflow: hidden;
display: flex;
flex-wrap: nowrap;
align-items: center;
width: 12rem;
box-shadow: inset -1px 0 0 0 rgba(0,0,0,0.33);
}
.tab[data-active] {
background: var(--tab-color);
}
.tab .favicon[src] {
display: none;
width: 16px;
height: 16px;
margin-right: 0.2rem;
}
.tab .favicon[src].loaded {
display: block;
}
.tab .title {
white-space: nowrap;
flex: 1 1 auto;
min-width: 0;
text-overflow: ellipsis;
overflow: hidden;
font-size: 0.725rem;
user-select: none;
}
.tab .controls {
flex: 0 0 auto;
font-size: 0;
}
.tab .controls .control {
background: rgba(0,0,0,0.2);
border: none;
border-radius: 50%;
padding: 0;
margin-left: 0.2rem;
width: 1rem;
height: 1rem;
color: #aaa;
font-size: 0.7rem;
vertical-align: middle;
line-height: 0;
}
.tab .controls .control:disabled {
display: none;
}
.app-drag {
flex: 1 0 auto;
-webkit-app-region: drag;
height: calc(100% - 5px);
min-width: 2rem;
align-self: flex-end;
}
.window-controls {
display: none;
flex-direction: row;
flex: 0 0 auto;
}
.platform-linux .window-controls {
display: flex;
}
.window-controls .control {
width: 2.5rem;
background: none;
border: none;
color: #fff;
}
.window-controls .control:hover {
background: rgba(255, 255, 255, 0.2);
}
/*----- TOOLBAR -----*/
.toolbar {
height: 1.875rem;
background-color: var(--tab-color);
display: flex;
align-items: center;
padding: 0.2rem 0.5rem;
}
.toolbar .page-controls {
margin-right: 0.5rem;
}
.address-bar {
flex: 1 0 auto;
line-height: 0;
height: 100%;
}
.address-bar input {
width: 100%;
height: 100%;
background: var(--bg-color);
color: var(--text-color);
border: none;
border-radius: 2rem;
padding: 0 0.5rem;
outline: none;
}
.toolbar .control {
background: none;
border: none;
width: 1.5rem;
height: 1.5rem;
padding: 0;
line-height: 0;
}
</style>
</head>
<body>
<div class="topbar">
<div id="tabstrip">
<div class="tab-container">
<ul class="tab-list">
<template id="tabtemplate">
<li class="tab">
<img class="favicon" />
<span class="title"></span>
<div class="controls">
<button class="control audio" disabled>🔊</button>
<button class="control close"></button>
</div>
</li>
</template>
</ul>
<button id="createtab">+</button>
<div class="app-drag"></div>
<div class="window-controls">
<button id="minimize" class="control">🗕</button>
<button id="maximize" class="control">🗖</button>
<button id="close" class="control">🗙</button>
</div>
</div>
</div>
<div class="toolbar">
<div class="page-controls">
<button id="goback" class="control">⬅️</button>
<button id="goforward" class="control">➡️</button>
<button id="reload" class="control">🔄</button>
</div>
<div class="address-bar">
<input id="addressurl" spellcheck="false" />
</div>
<browser-action-list id="actions" alignment="bottom left"></browser-action-list>
</div>
</div>
</div>
<script src="./webui.js"></script>
</body>
</html>

View File

@@ -0,0 +1,193 @@
class WebUI {
windowId = -1
activeTabId = -1
/** @type {chrome.tabs.Tab[]} */
tabList = []
constructor() {
const $ = document.querySelector.bind(document)
this.$ = {
tabList: $('#tabstrip .tab-list'),
tabTemplate: $('#tabtemplate'),
createTabButton: $('#createtab'),
goBackButton: $('#goback'),
goForwardButton: $('#goforward'),
reloadButton: $('#reload'),
addressUrl: $('#addressurl'),
browserActions: $('#actions'),
minimizeButton: $('#minimize'),
maximizeButton: $('#maximize'),
closeButton: $('#close'),
}
this.$.createTabButton.addEventListener('click', () => chrome.tabs.create())
this.$.goBackButton.addEventListener('click', () => chrome.tabs.goBack())
this.$.goForwardButton.addEventListener('click', () => chrome.tabs.goForward())
this.$.reloadButton.addEventListener('click', () => chrome.tabs.reload())
this.$.addressUrl.addEventListener('keypress', this.onAddressUrlKeyPress.bind(this))
this.$.minimizeButton.addEventListener('click', () =>
chrome.windows.get(chrome.windows.WINDOW_ID_CURRENT, (win) => {
chrome.windows.update(win.id, { state: win.state === 'minimized' ? 'normal' : 'minimized' })
}),
)
this.$.maximizeButton.addEventListener('click', () =>
chrome.windows.get(chrome.windows.WINDOW_ID_CURRENT, (win) => {
chrome.windows.update(win.id, { state: win.state === 'maximized' ? 'normal' : 'maximized' })
}),
)
this.$.closeButton.addEventListener('click', () => chrome.windows.remove())
const platformClass = `platform-${navigator.userAgentData.platform.toLowerCase()}`
document.body.classList.add(platformClass)
this.initTabs()
}
async initTabs() {
const tabs = await new Promise((resolve) => chrome.tabs.query({ windowId: -2 }, resolve))
this.tabList = [...tabs]
this.renderTabs()
const activeTab = this.tabList.find((tab) => tab.active)
if (activeTab) {
this.setActiveTab(activeTab)
}
// Wait to setup tabs and windowId prior to listening for updates.
this.setupBrowserListeners()
}
setupBrowserListeners() {
if (!chrome.tabs.onCreated) {
throw new Error(`chrome global not setup. Did the extension preload not get run?`)
}
const findTab = (tabId) => {
const existingTab = this.tabList.find((tab) => tab.id === tabId)
return existingTab
}
const findOrCreateTab = (tabId) => {
const existingTab = findTab(tabId)
if (existingTab) return existingTab
const newTab = { id: tabId }
this.tabList.push(newTab)
return newTab
}
chrome.tabs.onCreated.addListener((tab) => {
if (tab.windowId !== this.windowId) return
const newTab = findOrCreateTab(tab.id)
Object.assign(newTab, tab)
this.renderTabs()
})
chrome.tabs.onActivated.addListener((activeInfo) => {
if (activeInfo.windowId !== this.windowId) return
this.setActiveTab(activeInfo)
})
chrome.tabs.onUpdated.addListener((tabId, changeInfo, details) => {
const tab = findTab(tabId)
if (!tab) return
Object.assign(tab, details)
this.renderTabs()
if (tabId === this.activeTabId) this.renderToolbar(tab)
})
chrome.tabs.onRemoved.addListener((tabId) => {
const tabIndex = this.tabList.findIndex((tab) => tab.id === tabId)
if (tabIndex > -1) {
this.tabList.splice(tabIndex, 1)
this.$.tabList.querySelector(`[data-tab-id="${tabId}"]`).remove()
}
})
}
setActiveTab(activeTab) {
this.activeTabId = activeTab.id || activeTab.tabId
this.windowId = activeTab.windowId
for (const tab of this.tabList) {
if (tab.id === this.activeTabId) {
tab.active = true
this.renderTab(tab)
this.renderToolbar(tab)
} else {
if (tab.active) {
tab.active = false
this.renderTab(tab)
}
}
}
}
onAddressUrlKeyPress(event) {
if (event.code === 'Enter') {
const url = this.$.addressUrl.value
chrome.tabs.update({ url })
}
}
createTabNode(tab) {
const tabElem = this.$.tabTemplate.content.cloneNode(true).firstElementChild
tabElem.dataset.tabId = tab.id
tabElem.addEventListener('click', () => {
chrome.tabs.update(tab.id, { active: true })
})
tabElem.querySelector('.close').addEventListener('click', () => {
chrome.tabs.remove(tab.id)
})
const faviconElem = tabElem.querySelector('.favicon')
faviconElem?.addEventListener('load', () => {
faviconElem.classList.toggle('loaded', true)
})
faviconElem?.addEventListener('error', () => {
faviconElem.classList.toggle('loaded', false)
})
this.$.tabList.appendChild(tabElem)
return tabElem
}
renderTab(tab) {
let tabElem = this.$.tabList.querySelector(`[data-tab-id="${tab.id}"]`)
if (!tabElem) tabElem = this.createTabNode(tab)
if (tab.active) {
tabElem.dataset.active = ''
} else {
delete tabElem.dataset.active
}
const favicon = tabElem.querySelector('.favicon')
if (tab.favIconUrl) {
favicon.src = tab.favIconUrl
} else {
delete favicon.src
}
tabElem.querySelector('.title').textContent = tab.title
tabElem.querySelector('.audio').disabled = !tab.audible
}
renderTabs() {
this.tabList.forEach((tab) => {
this.renderTab(tab)
})
}
renderToolbar(tab) {
this.$.addressUrl.value = tab.url
// this.$.browserActions.tab = tab.id
}
}
window.webui = new WebUI()