Commit 7f03fb1b authored by Domi's avatar Domi

fix: popup minimize

parent 1ee1ea77
...@@ -7,10 +7,8 @@ ...@@ -7,10 +7,8 @@
<title>Anything Copilot DEV</title> <title>Anything Copilot DEV</title>
</head> </head>
<body> <body>
<div id="app"></div> <div id="app">
<div> <iframe class="w-screen h-screen" src="https://bing.com"></iframe>
<div class="h-screen"></div>
<div class="h-screen"></div>
</div> </div>
<script type="module" src="./src/pages/dev.ts"></script> <script type="module" src="./src/pages/dev.ts"></script>
</body> </body>
......
{ {
"data": { "data": {
"configVersion": 20240330, "configVersion": 20240406,
"chatDocSites": [ "chatDocSites": [
{ {
"host": "huggingface.co", "host": "huggingface.co",
...@@ -140,7 +140,8 @@ ...@@ -140,7 +140,8 @@
"defaultUA": "", "defaultUA": "",
"hostUA": { "hostUA": {
"www.google.com": 1, "www.google.com": 1,
"bing.com": 1 "bing.com": 1,
"copilot.microsoft.com": 0
} }
} }
} }
......
...@@ -4,8 +4,8 @@ import { ...@@ -4,8 +4,8 @@ import {
type ParseDocOptions, type ParseDocOptions,
ContentScriptId, ContentScriptId,
} from "@/types" } from "@/types"
import { waitMessage, tabUpdated, getLocal } from "@/utils/ext" import { waitMessage, tabUpdated, getLocal, getPipWindow } from "@/utils/ext"
import { offscreen } from "./offscreen" import { setupOffscreenDocument, offscreenHtmlPath } from "./offscreen"
import { import {
registerContentSidebar, registerContentSidebar,
unregisterContentSidebar, unregisterContentSidebar,
...@@ -14,6 +14,7 @@ import { ...@@ -14,6 +14,7 @@ import {
import config from "@/assets/config.json" import config from "@/assets/config.json"
import { allFrameScript, contentMainScript } from "@/manifest" import { allFrameScript, contentMainScript } from "@/manifest"
import { getIsEdge } from "@/utils/ext" import { getIsEdge } from "@/utils/ext"
import { contentInvoke } from "@/utils/invoke"
type Config = typeof config type Config = typeof config
...@@ -69,33 +70,6 @@ async function pipLaunch(url: string) { ...@@ -69,33 +70,6 @@ async function pipLaunch(url: string) {
}) })
} }
type QueryOptions = {
windowId?: number
width?: number
height?: number
}
async function getPipWindow(
id: number,
{ windowId, width, height }: QueryOptions
) {
if (windowId) {
const win = await chrome.windows.get(windowId)
chrome.tabs.sendMessage(id, {
type: MessageType.pipWinInfo,
window: win,
})
return win
}
const windows = await chrome.windows.getAll({})
const win = windows.find((w) => w.width === width && w.height === height)
chrome.tabs.sendMessage(id, {
type: MessageType.pipWinInfo,
window: win,
})
return win
}
type UpdatePipWinOption = { type UpdatePipWinOption = {
windowId: number windowId: number
windowInfo: Partial<chrome.windows.UpdateInfo> windowInfo: Partial<chrome.windows.UpdateInfo>
...@@ -105,42 +79,38 @@ async function updateWindow({ windowId, windowInfo }: UpdatePipWinOption) { ...@@ -105,42 +79,38 @@ async function updateWindow({ windowId, windowInfo }: UpdatePipWinOption) {
await chrome.windows.update(windowId, windowInfo) await chrome.windows.update(windowId, windowInfo)
} }
let currentSender: chrome.runtime.MessageSender | null = null
contentInvoke
.register(ServiceFunc.setupOffscreen, () =>
setupOffscreenDocument(offscreenHtmlPath)
)
.register(ServiceFunc.getAllCommands, () => chrome.commands.getAll())
.register(ServiceFunc.createTab, (p: chrome.tabs.CreateProperties) =>
chrome.tabs.create(p)
)
.register(ServiceFunc.getPipWindow, getPipWindow)
.register(ServiceFunc.getMyTab, () =>
chrome.tabs.get(currentSender!.tab!.id!)
)
async function handleInvokeRequest( async function handleInvokeRequest(
message: any, message: any,
sender: chrome.runtime.MessageSender sender: chrome.runtime.MessageSender
) { ) {
const { key, func, args } = message currentSender = sender
let result = await contentInvoke.handleReqMsg(message)
let result = null
let error = null
try {
switch (func) {
case ServiceFunc.parseDoc:
case ServiceFunc.calcTokens:
case ServiceFunc.tokenSlice:
result = await offscreen.invoke({
func,
args,
})
break
}
} catch (err) {
console.error("invoke error: ", err)
error = err
}
console.log("invoke response: ", result, error)
if (!sender.tab?.id) { if (!sender.tab?.id) {
console.error("sender tab id is undefined", sender) console.error("sender tab id is undefined", sender)
} }
if (result) {
chrome.tabs.sendMessage(sender.tab?.id!, { chrome.tabs.sendMessage(sender.tab?.id!, {
type: MessageType.invokeResponse, type: MessageType.invokeResponse,
key, ...result,
success: !error,
payload: !error ? result : error,
}) })
}
} }
function handleMessage(message: any, sender: chrome.runtime.MessageSender) { function handleMessage(message: any, sender: chrome.runtime.MessageSender) {
...@@ -152,19 +122,15 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender) { ...@@ -152,19 +122,15 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender) {
case MessageType.bgPipLaunch: case MessageType.bgPipLaunch:
pipLaunch(message.url) pipLaunch(message.url)
break break
case MessageType.getPipWinInfo:
getPipWindow(sender.tab?.id!, message.options)
break
case MessageType.updateWindow: case MessageType.updateWindow:
updateWindow(message.options) updateWindow(message.options)
break break
case MessageType.removeWindow: case MessageType.removeWindow:
chrome.windows.remove(message.options.windowId) chrome.windows.remove(message.options.windowId)
break break
case MessageType.setupOffscreenDocument: case MessageType.forwardToTab:
return offscreen.setup() chrome.tabs.sendMessage(message.tabId, message.message)
case MessageType.fromOffscreen: break
return offscreen.handleResMsg(message)
case MessageType.invokeRequest: case MessageType.invokeRequest:
handleInvokeRequest(message, sender) handleInvokeRequest(message, sender)
break break
...@@ -181,12 +147,17 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender) { ...@@ -181,12 +147,17 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender) {
} }
async function handleToggleMinimize() { async function handleToggleMinimize() {
const { pipWindowId } = await chrome.storage.local.get({ pipWindowId: null }) const { pipWindow } = await chrome.storage.local.get({ pipWindow: null })
if (!pipWindowId) return if (!pipWindow) return
const windowInfo = await chrome.windows.get(pipWindowId) const windowInfo = await chrome.windows.get(pipWindow.windowId)
if (!windowInfo) return if (!windowInfo) return
await chrome.windows.update(pipWindowId, {
state: windowInfo.state == "minimized" ? "normal" : "minimized", const tabId = pipWindow.tabId
contentInvoke.invoke({
tabId,
func: ServiceFunc.toggleMinimize,
args: [],
}) })
} }
......
import { MessageType, ServiceFunc, type ParseDocOptions } from "@/types"
import { Invoke } from "@/utils/invoke"
let creating: Promise<void> | null // A global promise to avoid concurrency issues let creating: Promise<void> | null // A global promise to avoid concurrency issues
export async function setupOffscreenDocument(path: string) { export async function setupOffscreenDocument(path: string) {
...@@ -37,39 +34,3 @@ export async function setupOffscreenDocument(path: string) { ...@@ -37,39 +34,3 @@ export async function setupOffscreenDocument(path: string) {
} }
export const offscreenHtmlPath = "/offscreen.html" export const offscreenHtmlPath = "/offscreen.html"
class Offscreen extends Invoke {
public readonly path: string
constructor(path: string) {
super("offscreen")
this.path = path
}
public async send(req: any): Promise<{ key: string; response: any }> {
const key = this.key
await this.setup()
console.log("offscreen send: ", key, req)
const response = await chrome.runtime.sendMessage({
type: MessageType.toOffscreen,
key,
...req,
})
return { key, response }
}
public handleResMsg(message: any): void {
const { type, key, payload, success } = message
if (type === MessageType.fromOffscreen) {
this.setReturnValue(key, success, payload)
}
}
public setup() {
return setupOffscreenDocument(this.path)
}
}
export const offscreen = new Offscreen(offscreenHtmlPath)
This diff is collapsed.
...@@ -145,12 +145,6 @@ async function loadFrame(url: string, ua?: string) { ...@@ -145,12 +145,6 @@ async function loadFrame(url: string, ua?: string) {
// iframe.srcdoc = html // iframe.srcdoc = html
} }
} }
iframe.contentWindow?.postMessage(
{
type: FrameMessageType.webviewRun,
},
"*"
)
} }
function handleFrameMessage(e: MessageEvent) { function handleFrameMessage(e: MessageEvent) {
...@@ -162,13 +156,10 @@ function handleFrameMessage(e: MessageEvent) { ...@@ -162,13 +156,10 @@ function handleFrameMessage(e: MessageEvent) {
switch (type) { switch (type) {
case FrameMessageType.pageInfo: case FrameMessageType.pageInfo:
if (!pageInfo.url) {
pageInfo.url = e.data.url pageInfo.url = e.data.url
pageInfo.title = e.data.title pageInfo.title = e.data.title
pageInfo.icon = e.data.icon pageInfo.icon = e.data.icon
emit("load", pageInfo) emit("load", pageInfo)
}
break break
case FrameMessageType.invokeResponse: case FrameMessageType.invokeResponse:
webviewInvoke.value?.handleResMsg(e.data) webviewInvoke.value?.handleResMsg(e.data)
......
...@@ -171,7 +171,7 @@ onUnmounted(() => { ...@@ -171,7 +171,7 @@ onUnmounted(() => {
</div> </div>
<p class="text-center text-sm mt-2">{{ t("chatDocs.supportFormat") }}</p> <p class="text-center text-sm mt-2">{{ t("chatDocs.supportFormat") }}</p>
<div :class="['absolute text-xs flex items-center bottom-2 right-2']"> <div :class="['absolute text-xs flex items-center bottom-2 right-2']">
<img :src="logoUrl" class="w-3 h-3" /> <img :src="logoUrl" class="size-3" />
</div> </div>
</div> </div>
</div> </div>
......
...@@ -126,6 +126,7 @@ watch( ...@@ -126,6 +126,7 @@ watch(
if (maxInputType !== "token") return if (maxInputType !== "token") return
if (!message) return if (!message) return
await contentInvoke.setupOffscreen()
const tokenLength = await contentInvoke.calcTokens(message) const tokenLength = await contentInvoke.calcTokens(message)
const rate = (message.length / tokenLength) * 0.95 const rate = (message.length / tokenLength) * 0.95
const exceedMaxInput = tokenLength > maxInput const exceedMaxInput = tokenLength > maxInput
...@@ -172,6 +173,7 @@ watch( ...@@ -172,6 +173,7 @@ watch(
if (item.kind == "file" && typeof item.data != "string") { if (item.kind == "file" && typeof item.data != "string") {
const url = await convertBlobToBase64(item.data) const url = await convertBlobToBase64(item.data)
await contentInvoke.setupOffscreen()
const results = await contentInvoke.parseDoc({ const results = await contentInvoke.parseDoc({
key: item.key, key: item.key,
type: item.type, type: item.type,
...@@ -640,4 +642,5 @@ input:hover { ...@@ -640,4 +642,5 @@ input:hover {
transform: translate(100%, 0); transform: translate(100%, 0);
} }
} }
</style>@/utils/const@/utils/invoke/service@/utils/invokeb </style>
\ No newline at end of file @/utils/const@/utils/invoke/service@/utils/invokeb
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 -960 960 960"
width="24"
fill="currentColor"
>
<path
d="M120-120v-170l528-527q12-11 26.5-17t30.5-6q16 0 31 6t26 18l55 56q12 11 17.5 26t5.5 30q0 16-5.5 30.5T817-647L290-120H120Zm584-528 56-56-56-56-56 56 56 56Z"
/>
</svg>
</template>
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 -960 960 960"
width="24"
fill="currentColor"
>
<path
d="M160-200q-33 0-56.5-23.5T80-280v-400q0-33 23.5-56.5T160-760h640q33 0 56.5 23.5T880-680v400q0 33-23.5 56.5T800-200H160Zm160-120h320v-80H320v80ZM200-440h80v-80h-80v80Zm120 0h80v-80h-80v80Zm120 0h80v-80h-80v80Zm120 0h80v-80h-80v80Zm120 0h80v-80h-80v80ZM200-560h80v-80h-80v80Zm120 0h80v-80h-80v80Zm120 0h80v-80h-80v80Zm120 0h80v-80h-80v80Zm120 0h80v-80h-80v80Z"
/>
</svg>
</template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from "vue" import { ref, onMounted, onUnmounted, watch } from "vue"
import { pipWindow } from "@/store/content" import { pipWindow } from "@/store/popup"
import IconHide from "@/components/icons/IconHide.vue" import IconHide from "@/components/icons/IconHide.vue"
import IconArrowCircleRight from "@/components/icons/IconArrowCircleRight.vue" import IconArrowCircleRight from "@/components/icons/IconArrowCircleRight.vue"
import IconClose from "@/components/icons/IconClose.vue" import IconClose from "@/components/icons/IconClose.vue"
import { MessageType } from "@/types" import { MessageType, ServiceFunc } from "@/types"
import { computed } from "vue"
import { contentInvoke } from "@/utils/invoke"
import { handleImgError } from "@/utils/dom"
async function handleUpdatePip(state: "normal" | "minimized") { const globeImg = chrome.runtime.getURL("img/globe.svg")
await chrome.runtime.sendMessage({
type: MessageType.updateWindow, const isMinimized = computed(() => {
options: { if (pipWindow.windowsWindow) {
windowId: pipWindow.id, const { width, height } = pipWindow.windowsWindow
windowInfo: { return width! < 300 && height! < 100
state, }
}, return false
}, })
async function toggleMinimize() {
await contentInvoke.invoke({
tabId: pipWindow.tabId,
func: ServiceFunc.toggleMinimize,
args: [],
}) })
if (pipWindow.id) {
const win = await chrome.windows.get(pipWindow.id)
pipWindow.windowsWindow = win
}
} }
async function closePip() { async function closePip() {
pipWindow.windowsWindow = null
await chrome.runtime.sendMessage({ await chrome.runtime.sendMessage({
type: MessageType.updateWindow, type: MessageType.updateWindow,
options: { options: {
...@@ -40,24 +56,25 @@ async function closePip() { ...@@ -40,24 +56,25 @@ async function closePip() {
<template> <template>
<div class="justify-between border-2 border-solid border-background-mute"> <div class="justify-between border-2 border-solid border-background-mute">
<div <img
:src="pipWindow.icon"
:data-fallback="globeImg"
class="size-7 rounded mr-auto" class="size-7 rounded mr-auto"
:style="{ loading="lazy"
background: `#8881 center / contain url('${pipWindow.tab?.favIconUrl}')`, @error="handleImgError"
}" />
></div>
<button <button
v-if="pipWindow.windowsWindow?.state === 'normal'" v-if="!isMinimized"
class="bg-background-soft hover:bg-background-mute rounded-full size-8 p-1 flex items-center justify-center" class="bg-background-soft hover:bg-background-mute rounded-full size-8 p-1 flex items-center justify-center"
@click="handleUpdatePip('minimized')" @click="toggleMinimize"
> >
<IconHide class="size-5" /> <IconHide class="size-5" />
</button> </button>
<button <button
v-if="pipWindow.windowsWindow?.state === 'minimized'" v-else
class="bg-background-soft hover:bg-background-mute rounded-full size-8 p-1 flex items-center justify-center" class="bg-background-soft hover:bg-background-mute rounded-full size-8 p-1 flex items-center justify-center"
@click="handleUpdatePip('normal')" @click="toggleMinimize"
> >
<IconArrowCircleRight class="size-5" /> <IconArrowCircleRight class="size-5" />
</button> </button>
......
...@@ -4,6 +4,11 @@ import PipSplash from "@/components/PipSplash.vue" ...@@ -4,6 +4,11 @@ import PipSplash from "@/components/PipSplash.vue"
import { pipLoading } from "@/store/content" import { pipLoading } from "@/store/content"
import LoadingBar from "@/components/LoadingBar.vue" import LoadingBar from "@/components/LoadingBar.vue"
import ChatDocsAddon from "@/components/chatdocs/ChatDocsAddon.vue" import ChatDocsAddon from "@/components/chatdocs/ChatDocsAddon.vue"
// const isDev =
// process.env.NODE_ENV === "development" &&
// location.host == chrome.runtime.id &&
// location.pathname == "/dev.html"
</script> </script>
<template> <template>
......
...@@ -10,6 +10,7 @@ import { ...@@ -10,6 +10,7 @@ import {
ContentEventType, ContentEventType,
FrameMessageType, FrameMessageType,
MessageType, MessageType,
ServiceFunc,
WebviewFunc, WebviewFunc,
WindowName, WindowName,
} from "@/types" } from "@/types"
...@@ -45,12 +46,6 @@ function handleMessage( ...@@ -45,12 +46,6 @@ function handleMessage(
}) })
sendResponse({ type: MessageType.contentHere }) sendResponse({ type: MessageType.contentHere })
break break
case MessageType.pipWinInfo:
pipWindow.windowsWindow = message.window
chrome.storage.local.set({
pipWindowId: message.window.id,
})
break
case MessageType.invokeResponse: case MessageType.invokeResponse:
contentInvoke.handleResMsg(message) contentInvoke.handleResMsg(message)
break break
...@@ -62,6 +57,16 @@ function handleMessage( ...@@ -62,6 +57,16 @@ function handleMessage(
sidebarAddon.hidden = false sidebarAddon.hidden = false
sidebarAddon.url = message.url sidebarAddon.url = message.url
break break
case MessageType.invokeRequest:
contentInvoke.handleReqMsg(message).then((result) => {
if (result) {
chrome.runtime.sendMessage({
type: MessageType.invokeResponse,
...result,
})
}
})
break
} }
} }
...@@ -90,18 +95,33 @@ async function handlePipEvent(event: any) { ...@@ -90,18 +95,33 @@ async function handlePipEvent(event: any) {
addContentEventListener(ContentEventType.pipLoaded, handlePipLoaded) addContentEventListener(ContentEventType.pipLoaded, handlePipLoaded)
}) })
// may be 0 if not wait document is loaded const window = await contentInvoke.invoke({
chrome.runtime.sendMessage({ func: ServiceFunc.getPipWindow,
type: MessageType.getPipWinInfo, args: [
options: { {
width: win.outerWidth, width: win.outerWidth,
height: win.outerHeight, height: win.outerHeight,
}, },
],
})
const tab = await contentInvoke.invoke({
func: ServiceFunc.getMyTab,
args: [],
})
pipWindow.windowsWindow = window
chrome.storage.local.set({
pipWindow: {
windowId: window.id,
tabId: tab.id,
icon: getPageIcon(),
},
}) })
win.addEventListener("pagehide", () => { win.addEventListener("pagehide", () => {
chrome.storage.local.set({ chrome.storage.local.set({
pipWindowId: 0, pipWindow: null,
}) })
}) })
} }
...@@ -146,10 +166,6 @@ function handleFrameMessage(e: MessageEvent) { ...@@ -146,10 +166,6 @@ function handleFrameMessage(e: MessageEvent) {
if (!e.data || typeof e.data !== "object") return if (!e.data || typeof e.data !== "object") return
const type = e.data.type const type = e.data.type
switch (type) { switch (type) {
case FrameMessageType.webviewRun:
run()
postPageInfo()
return
case FrameMessageType.escapeLoad: case FrameMessageType.escapeLoad:
return dispatchContentEvent({ return dispatchContentEvent({
type: ContentEventType.escapeLoad, type: ContentEventType.escapeLoad,
...@@ -207,9 +223,13 @@ if (window.top !== window && window.name.startsWith(WindowName.webview)) { ...@@ -207,9 +223,13 @@ if (window.top !== window && window.name.startsWith(WindowName.webview)) {
}, },
chrome.runtime.getURL("") chrome.runtime.getURL("")
) )
run()
postPageInfo()
} }
// dev // dev
if (location.host == chrome.runtime.id && location.hash == "#copilot") { if (location.host == chrome.runtime.id && location.hash == "#copilot") {
pipWindow.window = window
mount(Copilot, window.document) mount(Copilot, window.document)
} }
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "ይህ ገጽ ራስ-ሰር መላክን አይደግፍም. እባክዎ መልዕክቱን ይቅዱ እና እራስዎ ይላኩ." "notSupported": "ይህ ገጽ ራስ-ሰር መላክን አይደግፍም. እባክዎ መልዕክቱን ይቅዱ እና እራስዎ ይላኩ."
}, },
"openSidebar": "የጎን አሞሌውን ይክፈቱ", "openSidebar": "የጎን አሞሌውን ይክፈቱ",
"searchPlaceholder": "ዩአርኤል ይፈልጉ ወይም ይተይቡ" "searchPlaceholder": "ዩአርኤል ይፈልጉ ወይም ይተይቡ",
"setShortcutKeys": "አቋራጭ ቁልፎችን አዘጋጅ",
"mobileView": "የሞባይል እይታ"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "هذه الصفحة لا تدعم الإرسال التلقائي. يرجى نسخ الرسالة وإرسالها يدويًا." "notSupported": "هذه الصفحة لا تدعم الإرسال التلقائي. يرجى نسخ الرسالة وإرسالها يدويًا."
}, },
"openSidebar": "افتح الشريط الجانبي", "openSidebar": "افتح الشريط الجانبي",
"searchPlaceholder": "ابحث عن عنوان URL أو اكتبه" "searchPlaceholder": "ابحث عن عنوان URL أو اكتبه",
"setShortcutKeys": "تعيين مفاتيح الاختصار",
"mobileView": "واجهه جوال"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Тази страница не поддържа автоматично изпращане. Моля, копирайте съобщението и го изпратете ръчно." "notSupported": "Тази страница не поддържа автоматично изпращане. Моля, копирайте съобщението и го изпратете ръчно."
}, },
"openSidebar": "Отворете страничната лента", "openSidebar": "Отворете страничната лента",
"searchPlaceholder": "Потърсете или въведете URL" "searchPlaceholder": "Потърсете или въведете URL",
"setShortcutKeys": "Задаване на клавишни комбинации",
"mobileView": "Мобилен изглед"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "এই পৃষ্ঠাটি স্বয়ংক্রিয় প্রেরণকে সমর্থন করে না। দয়া করে বার্তাটি অনুলিপি করুন এবং এটি ম্যানুয়ালি প্রেরণ করুন।" "notSupported": "এই পৃষ্ঠাটি স্বয়ংক্রিয় প্রেরণকে সমর্থন করে না। দয়া করে বার্তাটি অনুলিপি করুন এবং এটি ম্যানুয়ালি প্রেরণ করুন।"
}, },
"openSidebar": "সাইডবারটি খুলুন", "openSidebar": "সাইডবারটি খুলুন",
"searchPlaceholder": "একটি URL অনুসন্ধান করুন বা টাইপ করুন" "searchPlaceholder": "একটি URL অনুসন্ধান করুন বা টাইপ করুন",
"setShortcutKeys": "শর্টকাট কী সেট করুন",
"mobileView": "মোবাইল দৃশ্য"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Aquesta pàgina no admet l'enviament automàtic. Copieu el missatge i envieu -lo manualment." "notSupported": "Aquesta pàgina no admet l'enviament automàtic. Copieu el missatge i envieu -lo manualment."
}, },
"openSidebar": "Obriu la barra lateral", "openSidebar": "Obriu la barra lateral",
"searchPlaceholder": "Cerqueu o escriviu un URL" "searchPlaceholder": "Cerqueu o escriviu un URL",
"setShortcutKeys": "Estableix tecles de drecera",
"mobileView": "Vista mòbil"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Tato stránka nepodporuje automatické odesílání. Zkopírujte zprávu a odešlete ji ručně." "notSupported": "Tato stránka nepodporuje automatické odesílání. Zkopírujte zprávu a odešlete ji ručně."
}, },
"openSidebar": "Otevřete postranní panel", "openSidebar": "Otevřete postranní panel",
"searchPlaceholder": "Vyhledejte nebo zadejte adresu URL" "searchPlaceholder": "Vyhledejte nebo zadejte adresu URL",
"setShortcutKeys": "Nastavit klávesové zkratky",
"mobileView": "Mobilní pohled"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Denne side understøtter ikke automatisk afsendelse. Kopier meddelelsen og send den manuelt." "notSupported": "Denne side understøtter ikke automatisk afsendelse. Kopier meddelelsen og send den manuelt."
}, },
"openSidebar": "Åbn sidebjælken", "openSidebar": "Åbn sidebjælken",
"searchPlaceholder": "Søg eller skriv en URL" "searchPlaceholder": "Søg eller skriv en URL",
"setShortcutKeys": "Indstil genvejstaster",
"mobileView": "Mobil visning"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Diese Seite unterstützt das automatische Senden nicht. Bitte kopieren Sie die Nachricht und senden Sie sie manuell." "notSupported": "Diese Seite unterstützt das automatische Senden nicht. Bitte kopieren Sie die Nachricht und senden Sie sie manuell."
}, },
"openSidebar": "Öffnen Sie die Seitenleiste", "openSidebar": "Öffnen Sie die Seitenleiste",
"searchPlaceholder": "Suchen oder geben Sie eine URL ein" "searchPlaceholder": "Suchen oder geben Sie eine URL ein",
"setShortcutKeys": "Legen Sie Tastenkombinationen fest",
"mobileView": "Mobile Ansicht"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Αυτή η σελίδα δεν υποστηρίζει αυτόματη αποστολή. Αντιγράψτε το μήνυμα και στείλτε το χειροκίνητα." "notSupported": "Αυτή η σελίδα δεν υποστηρίζει αυτόματη αποστολή. Αντιγράψτε το μήνυμα και στείλτε το χειροκίνητα."
}, },
"openSidebar": "Ανοίξτε την πλαϊνή μπάρα", "openSidebar": "Ανοίξτε την πλαϊνή μπάρα",
"searchPlaceholder": "Αναζητήστε ή πληκτρολογήστε μια διεύθυνση URL" "searchPlaceholder": "Αναζητήστε ή πληκτρολογήστε μια διεύθυνση URL",
"setShortcutKeys": "Ορισμός πλήκτρων συντόμευσης",
"mobileView": "Προβολή κινητού"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "This page does not support automatic sending. Please copy the message and send it manually." "notSupported": "This page does not support automatic sending. Please copy the message and send it manually."
}, },
"openSidebar": "Open Sidebar", "openSidebar": "Open Sidebar",
"searchPlaceholder": "Search or type a URL" "searchPlaceholder": "Search or type a URL",
"setShortcutKeys": "Set shortcut keys",
"mobileView": "Mobile View"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Esta página no admite el envío automático. Copie el mensaje y envíelo manualmente." "notSupported": "Esta página no admite el envío automático. Copie el mensaje y envíelo manualmente."
}, },
"openSidebar": "Abrir la barra lateral", "openSidebar": "Abrir la barra lateral",
"searchPlaceholder": "Buscar o escribir una URL" "searchPlaceholder": "Buscar o escribir una URL",
"setShortcutKeys": "Establecer teclas de acceso directo",
"mobileView": "Vista móvil"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Esta página no admite el envío automático. Copie el mensaje y envíelo manualmente." "notSupported": "Esta página no admite el envío automático. Copie el mensaje y envíelo manualmente."
}, },
"openSidebar": "Abrir la barra lateral", "openSidebar": "Abrir la barra lateral",
"searchPlaceholder": "Buscar o escribir una URL" "searchPlaceholder": "Buscar o escribir una URL",
"setShortcutKeys": "Establecer teclas de acceso directo",
"mobileView": "Vista móvil"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "See leht ei toeta automaatset saatmist. Kopeerige sõnum ja saatke see käsitsi." "notSupported": "See leht ei toeta automaatset saatmist. Kopeerige sõnum ja saatke see käsitsi."
}, },
"openSidebar": "Avage külgriba", "openSidebar": "Avage külgriba",
"searchPlaceholder": "Otsige või sisestage URL" "searchPlaceholder": "Otsige või sisestage URL",
"setShortcutKeys": "Määrake kiirklahvid",
"mobileView": "Mobiilivaade"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "این صفحه از ارسال خودکار پشتیبانی نمی کند. لطفا پیام را کپی کرده و به صورت دستی ارسال کنید." "notSupported": "این صفحه از ارسال خودکار پشتیبانی نمی کند. لطفا پیام را کپی کرده و به صورت دستی ارسال کنید."
}, },
"openSidebar": "نوار کناری را باز کنید", "openSidebar": "نوار کناری را باز کنید",
"searchPlaceholder": "یک URL را جستجو یا تایپ کنید" "searchPlaceholder": "یک URL را جستجو یا تایپ کنید",
"setShortcutKeys": "تنظیم کلیدهای میانبر",
"mobileView": "نمای موبایل"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Tämä sivu ei tue automaattista lähettämistä. Kopioi viesti ja lähetä se manuaalisesti." "notSupported": "Tämä sivu ei tue automaattista lähettämistä. Kopioi viesti ja lähetä se manuaalisesti."
}, },
"openSidebar": "Avaa sivupalkki", "openSidebar": "Avaa sivupalkki",
"searchPlaceholder": "Hae tai kirjoita URL-osoite" "searchPlaceholder": "Hae tai kirjoita URL-osoite",
"setShortcutKeys": "Aseta pikanäppäimet",
"mobileView": "Mobiilinäkymä"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Ang pahinang ito ay hindi sumusuporta sa awtomatikong pagpapadala. Mangyaring kopyahin ang mensahe at manu -manong ipadala ito." "notSupported": "Ang pahinang ito ay hindi sumusuporta sa awtomatikong pagpapadala. Mangyaring kopyahin ang mensahe at manu -manong ipadala ito."
}, },
"openSidebar": "Buksan ang sidebar", "openSidebar": "Buksan ang sidebar",
"searchPlaceholder": "Maghanap o mag-type ng URL" "searchPlaceholder": "Maghanap o mag-type ng URL",
"setShortcutKeys": "Itakda ang mga shortcut key",
"mobileView": "Mobile view"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Cette page ne prend pas en charge l'envoi automatique. Veuillez copier le message et l'envoyer manuellement." "notSupported": "Cette page ne prend pas en charge l'envoi automatique. Veuillez copier le message et l'envoyer manuellement."
}, },
"openSidebar": "Ouvrir la barre latérale", "openSidebar": "Ouvrir la barre latérale",
"searchPlaceholder": "Rechercher ou saisir une URL" "searchPlaceholder": "Rechercher ou saisir une URL",
"setShortcutKeys": "Définir les touches de raccourci",
"mobileView": "Vue mobile"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "આ પૃષ્ઠ સ્વચાલિત મોકલવાનું સમર્થન કરતું નથી. કૃપા કરીને સંદેશની નકલ કરો અને તેને જાતે મોકલો." "notSupported": "આ પૃષ્ઠ સ્વચાલિત મોકલવાનું સમર્થન કરતું નથી. કૃપા કરીને સંદેશની નકલ કરો અને તેને જાતે મોકલો."
}, },
"openSidebar": "સાઇડબાર ખોલો", "openSidebar": "સાઇડબાર ખોલો",
"searchPlaceholder": "URL શોધો અથવા લખો" "searchPlaceholder": "URL શોધો અથવા લખો",
"setShortcutKeys": "શોર્ટકટ કી સેટ કરો",
"mobileView": "મોબાઇલ દૃશ્ય"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "דף זה אינו תומך בשליחה אוטומטית. אנא העתק את ההודעה ושלח אותה ידנית." "notSupported": "דף זה אינו תומך בשליחה אוטומטית. אנא העתק את ההודעה ושלח אותה ידנית."
}, },
"openSidebar": "פתח את סרגל הצד", "openSidebar": "פתח את סרגל הצד",
"searchPlaceholder": "חפש או הקלד כתובת אתר" "searchPlaceholder": "חפש או הקלד כתובת אתר",
"setShortcutKeys": "הגדר מקשי קיצור",
"mobileView": "נוף נייד"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "यह पृष्ठ स्वचालित भेजने का समर्थन नहीं करता है। कृपया संदेश कॉपी करें और इसे मैन्युअल रूप से भेजें।" "notSupported": "यह पृष्ठ स्वचालित भेजने का समर्थन नहीं करता है। कृपया संदेश कॉपी करें और इसे मैन्युअल रूप से भेजें।"
}, },
"openSidebar": "साइडबार खोलें", "openSidebar": "साइडबार खोलें",
"searchPlaceholder": "URL खोजें या टाइप करें" "searchPlaceholder": "URL खोजें या टाइप करें",
"setShortcutKeys": "शॉर्टकट कुंजियाँ सेट करें",
"mobileView": "मोबाइल दृश्य"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Ova stranica ne podržava automatsko slanje. Kopirajte poruku i pošaljite je ručno." "notSupported": "Ova stranica ne podržava automatsko slanje. Kopirajte poruku i pošaljite je ručno."
}, },
"openSidebar": "Otvorite bočnu traku", "openSidebar": "Otvorite bočnu traku",
"searchPlaceholder": "Pretražite ili upišite URL" "searchPlaceholder": "Pretražite ili upišite URL",
"setShortcutKeys": "Postavite tipke prečaca",
"mobileView": "Mobilni prikaz"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Ez az oldal nem támogatja az automatikus küldéseket. Kérjük, másolja az üzenetet, és küldje el manuálisan." "notSupported": "Ez az oldal nem támogatja az automatikus küldéseket. Kérjük, másolja az üzenetet, és küldje el manuálisan."
}, },
"openSidebar": "Nyissa ki az oldalsávot", "openSidebar": "Nyissa ki az oldalsávot",
"searchPlaceholder": "Keressen vagy írjon be egy URL-t" "searchPlaceholder": "Keressen vagy írjon be egy URL-t",
"setShortcutKeys": "Gyorsbillentyűk beállítása",
"mobileView": "Mobil nézet"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Halaman ini tidak mendukung pengiriman otomatis. Harap salin pesan dan kirimkan secara manual." "notSupported": "Halaman ini tidak mendukung pengiriman otomatis. Harap salin pesan dan kirimkan secara manual."
}, },
"openSidebar": "Buka bilah samping", "openSidebar": "Buka bilah samping",
"searchPlaceholder": "Cari atau ketik URL" "searchPlaceholder": "Cari atau ketik URL",
"setShortcutKeys": "Setel tombol pintas",
"mobileView": "Tampilan seluler"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Questa pagina non supporta l'invio automatico. Si prega di copiare il messaggio e inviarlo manualmente." "notSupported": "Questa pagina non supporta l'invio automatico. Si prega di copiare il messaggio e inviarlo manualmente."
}, },
"openSidebar": "Apri la barra laterale", "openSidebar": "Apri la barra laterale",
"searchPlaceholder": "Cerca o digita un URL" "searchPlaceholder": "Cerca o digita un URL",
"setShortcutKeys": "Imposta i tasti di scelta rapida",
"mobileView": "Visualizzazione mobile"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "このページは、自動送信をサポートしていません。メッセージをコピーして手動で送信してください。" "notSupported": "このページは、自動送信をサポートしていません。メッセージをコピーして手動で送信してください。"
}, },
"openSidebar": "サイドバーを開きます", "openSidebar": "サイドバーを開きます",
"searchPlaceholder": "URLを検索または入力します" "searchPlaceholder": "URLを検索または入力します",
"setShortcutKeys": "ショートカットキーを設定する",
"mobileView": "モバイルビュー"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "ಈ ಪುಟವು ಸ್ವಯಂಚಾಲಿತ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ. ದಯವಿಟ್ಟು ಸಂದೇಶವನ್ನು ನಕಲಿಸಿ ಮತ್ತು ಅದನ್ನು ಕೈಯಾರೆ ಕಳುಹಿಸಿ." "notSupported": "ಈ ಪುಟವು ಸ್ವಯಂಚಾಲಿತ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ. ದಯವಿಟ್ಟು ಸಂದೇಶವನ್ನು ನಕಲಿಸಿ ಮತ್ತು ಅದನ್ನು ಕೈಯಾರೆ ಕಳುಹಿಸಿ."
}, },
"openSidebar": "ಸೈಡ್‌ಬಾರ್ ತೆರೆಯಿರಿ", "openSidebar": "ಸೈಡ್‌ಬಾರ್ ತೆರೆಯಿರಿ",
"searchPlaceholder": "URL ಅನ್ನು ಹುಡುಕಿ ಅಥವಾ ಟೈಪ್ ಮಾಡಿ" "searchPlaceholder": "URL ಅನ್ನು ಹುಡುಕಿ ಅಥವಾ ಟೈಪ್ ಮಾಡಿ",
"setShortcutKeys": "ಶಾರ್ಟ್‌ಕಟ್ ಕೀಗಳನ್ನು ಹೊಂದಿಸಿ",
"mobileView": "ಮೊಬೈಲ್ ವೀಕ್ಷಣೆ"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "이 페이지는 자동 전송을 지원하지 않습니다. 메시지를 복사하여 수동으로 보내주십시오." "notSupported": "이 페이지는 자동 전송을 지원하지 않습니다. 메시지를 복사하여 수동으로 보내주십시오."
}, },
"openSidebar": "사이드 바를 엽니 다", "openSidebar": "사이드 바를 엽니 다",
"searchPlaceholder": "URL을 검색하거나 입력하세요." "searchPlaceholder": "URL을 검색하거나 입력하세요.",
"setShortcutKeys": "단축키 설정",
"mobileView": "모바일 보기"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Šis puslapis nepalaiko automatinio siuntimo. Nukopijuokite pranešimą ir atsiųskite jį rankiniu būdu." "notSupported": "Šis puslapis nepalaiko automatinio siuntimo. Nukopijuokite pranešimą ir atsiųskite jį rankiniu būdu."
}, },
"openSidebar": "Atidarykite šoninę juostą", "openSidebar": "Atidarykite šoninę juostą",
"searchPlaceholder": "Ieškokite arba įveskite URL" "searchPlaceholder": "Ieškokite arba įveskite URL",
"setShortcutKeys": "Nustatykite sparčiuosius klavišus",
"mobileView": "Mobilusis vaizdas"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Šī lapa neatbalsta automātisku sūtīšanu. Lūdzu, nokopējiet ziņojumu un nosūtiet to manuāli." "notSupported": "Šī lapa neatbalsta automātisku sūtīšanu. Lūdzu, nokopējiet ziņojumu un nosūtiet to manuāli."
}, },
"openSidebar": "Atveriet sānjoslu", "openSidebar": "Atveriet sānjoslu",
"searchPlaceholder": "Meklējiet vai ierakstiet URL" "searchPlaceholder": "Meklējiet vai ierakstiet URL",
"setShortcutKeys": "Iestatiet īsinājumtaustiņus",
"mobileView": "Mobilais skats"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "ഈ പേജ് യാന്ത്രിക അയയ്ക്കുന്നതിനെ പിന്തുണയ്ക്കുന്നില്ല. സന്ദേശം പകർത്തി സ്വമേധയാ അയയ്ക്കുക." "notSupported": "ഈ പേജ് യാന്ത്രിക അയയ്ക്കുന്നതിനെ പിന്തുണയ്ക്കുന്നില്ല. സന്ദേശം പകർത്തി സ്വമേധയാ അയയ്ക്കുക."
}, },
"openSidebar": "സൈഡ്ബാർ തുറക്കുക", "openSidebar": "സൈഡ്ബാർ തുറക്കുക",
"searchPlaceholder": "ഒരു URL തിരയുക അല്ലെങ്കിൽ ടൈപ്പ് ചെയ്യുക" "searchPlaceholder": "ഒരു URL തിരയുക അല്ലെങ്കിൽ ടൈപ്പ് ചെയ്യുക",
"setShortcutKeys": "കുറുക്കുവഴി കീകൾ സജ്ജമാക്കുക",
"mobileView": "മൊബൈൽ കാഴ്ച"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "हे पृष्ठ स्वयंचलित पाठविण्यास समर्थन देत नाही. कृपया संदेश कॉपी करा आणि तो व्यक्तिचलितपणे पाठवा." "notSupported": "हे पृष्ठ स्वयंचलित पाठविण्यास समर्थन देत नाही. कृपया संदेश कॉपी करा आणि तो व्यक्तिचलितपणे पाठवा."
}, },
"openSidebar": "साइडबार उघडा", "openSidebar": "साइडबार उघडा",
"searchPlaceholder": "URL शोधा किंवा टाइप करा" "searchPlaceholder": "URL शोधा किंवा टाइप करा",
"setShortcutKeys": "शॉर्टकट की सेट करा",
"mobileView": "मोबाइल दृश्य"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Halaman ini tidak menyokong penghantaran automatik. Sila salin mesej dan hantarkan secara manual." "notSupported": "Halaman ini tidak menyokong penghantaran automatik. Sila salin mesej dan hantarkan secara manual."
}, },
"openSidebar": "Buka bar sisi", "openSidebar": "Buka bar sisi",
"searchPlaceholder": "Cari atau taip URL" "searchPlaceholder": "Cari atau taip URL",
"setShortcutKeys": "Tetapkan kekunci pintasan",
"mobileView": "Paparan mudah alih"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Deze pagina ondersteunt geen automatisch verzenden. Kopieer het bericht en stuur het handmatig." "notSupported": "Deze pagina ondersteunt geen automatisch verzenden. Kopieer het bericht en stuur het handmatig."
}, },
"openSidebar": "Open de zijbalk", "openSidebar": "Open de zijbalk",
"searchPlaceholder": "Zoek of typ een URL" "searchPlaceholder": "Zoek of typ een URL",
"setShortcutKeys": "Sneltoetsen instellen",
"mobileView": "Mobiele weergave"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Denne siden støtter ikke automatisk sending. Kopier meldingen og send den manuelt." "notSupported": "Denne siden støtter ikke automatisk sending. Kopier meldingen og send den manuelt."
}, },
"openSidebar": "Åpne sidefeltet", "openSidebar": "Åpne sidefeltet",
"searchPlaceholder": "Søk eller skriv inn en URL" "searchPlaceholder": "Søk eller skriv inn en URL",
"setShortcutKeys": "Angi snarveistaster",
"mobileView": "Mobil visning"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Ta strona nie obsługuje automatycznego wysyłania. Skopiuj wiadomość i wysyłaj ją ręcznie." "notSupported": "Ta strona nie obsługuje automatycznego wysyłania. Skopiuj wiadomość i wysyłaj ją ręcznie."
}, },
"openSidebar": "Otwórz pasek boczny", "openSidebar": "Otwórz pasek boczny",
"searchPlaceholder": "Wyszukaj lub wpisz adres URL" "searchPlaceholder": "Wyszukaj lub wpisz adres URL",
"setShortcutKeys": "Ustaw klawisze skrótu",
"mobileView": "Widok mobilny"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Esta página não suporta o envio automático. Copie a mensagem e envie -a manualmente." "notSupported": "Esta página não suporta o envio automático. Copie a mensagem e envie -a manualmente."
}, },
"openSidebar": "Abra a barra lateral", "openSidebar": "Abra a barra lateral",
"searchPlaceholder": "Pesquise ou digite um URL" "searchPlaceholder": "Pesquise ou digite um URL",
"setShortcutKeys": "Definir teclas de atalho",
"mobileView": "Exibição móvel"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Esta página não suporta o envio automático. Copie a mensagem e envie -a manualmente." "notSupported": "Esta página não suporta o envio automático. Copie a mensagem e envie -a manualmente."
}, },
"openSidebar": "Abra a barra lateral", "openSidebar": "Abra a barra lateral",
"searchPlaceholder": "Pesquise ou digite um URL" "searchPlaceholder": "Pesquise ou digite um URL",
"setShortcutKeys": "Definir teclas de atalho",
"mobileView": "Exibição móvel"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Această pagină nu acceptă trimiterea automată. Vă rugăm să copiați mesajul și să -l trimiteți manual." "notSupported": "Această pagină nu acceptă trimiterea automată. Vă rugăm să copiați mesajul și să -l trimiteți manual."
}, },
"openSidebar": "Deschideți bara laterală", "openSidebar": "Deschideți bara laterală",
"searchPlaceholder": "Căutați sau introduceți o adresă URL" "searchPlaceholder": "Căutați sau introduceți o adresă URL",
"setShortcutKeys": "Setați tastele de comandă rapidă",
"mobileView": "Vizualizare mobilă"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Эта страница не поддерживает автоматическую отправку. Пожалуйста, скопируйте сообщение и отправьте его вручную." "notSupported": "Эта страница не поддерживает автоматическую отправку. Пожалуйста, скопируйте сообщение и отправьте его вручную."
}, },
"openSidebar": "Откройте боковую панель", "openSidebar": "Откройте боковую панель",
"searchPlaceholder": "Найдите или введите URL-адрес" "searchPlaceholder": "Найдите или введите URL-адрес",
"setShortcutKeys": "Установить сочетания клавиш",
"mobileView": "Мобильная версия"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Táto stránka nepodporuje automatické odosielanie. Skopírujte správu a pošlite ju manuálne." "notSupported": "Táto stránka nepodporuje automatické odosielanie. Skopírujte správu a pošlite ju manuálne."
}, },
"openSidebar": "Otvorte bočný panel", "openSidebar": "Otvorte bočný panel",
"searchPlaceholder": "Vyhľadajte alebo zadajte adresu URL" "searchPlaceholder": "Vyhľadajte alebo zadajte adresu URL",
"setShortcutKeys": "Nastaviť klávesové skratky",
"mobileView": "Mobilný pohľad"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Ta stran ne podpira samodejnega pošiljanja. Kopirajte sporočilo in ga pošljite ročno." "notSupported": "Ta stran ne podpira samodejnega pošiljanja. Kopirajte sporočilo in ga pošljite ročno."
}, },
"openSidebar": "Odprite stransko vrstico", "openSidebar": "Odprite stransko vrstico",
"searchPlaceholder": "Iščite ali vnesite URL" "searchPlaceholder": "Iščite ali vnesite URL",
"setShortcutKeys": "Nastavite tipke za bližnjice",
"mobileView": "Mobilni pogled"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Ова страница не подржава аутоматско слање. Копирајте поруку и пошаљите га ручно." "notSupported": "Ова страница не подржава аутоматско слање. Копирајте поруку и пошаљите га ручно."
}, },
"openSidebar": "Отвори бочну траку", "openSidebar": "Отвори бочну траку",
"searchPlaceholder": "Претражите или унесите УРЛ" "searchPlaceholder": "Претражите или унесите УРЛ",
"setShortcutKeys": "Подесите пречице",
"mobileView": "Мобилни поглед"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Denna sida stöder inte automatisk sändning. Kopiera meddelandet och skicka det manuellt." "notSupported": "Denna sida stöder inte automatisk sändning. Kopiera meddelandet och skicka det manuellt."
}, },
"openSidebar": "Öppna sidofältet", "openSidebar": "Öppna sidofältet",
"searchPlaceholder": "Sök eller skriv en URL" "searchPlaceholder": "Sök eller skriv en URL",
"setShortcutKeys": "Ställ in kortkommandon",
"mobileView": "Mobilvy"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Ukurasa huu hauungi mkono kutuma moja kwa moja. Tafadhali nakili ujumbe na utumie kwa mikono." "notSupported": "Ukurasa huu hauungi mkono kutuma moja kwa moja. Tafadhali nakili ujumbe na utumie kwa mikono."
}, },
"openSidebar": "Fungua pembeni", "openSidebar": "Fungua pembeni",
"searchPlaceholder": "Tafuta au charaza URL" "searchPlaceholder": "Tafuta au charaza URL",
"setShortcutKeys": "Weka funguo za njia za mkato",
"mobileView": "Mwonekano wa rununu"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "இந்த பக்கம் தானியங்கி அனுப்புதலை ஆதரிக்காது. தயவுசெய்து செய்தியை நகலெடுத்து கைமுறையாக அனுப்புங்கள்." "notSupported": "இந்த பக்கம் தானியங்கி அனுப்புதலை ஆதரிக்காது. தயவுசெய்து செய்தியை நகலெடுத்து கைமுறையாக அனுப்புங்கள்."
}, },
"openSidebar": "பக்கப்பட்டியைத் திறக்கவும்", "openSidebar": "பக்கப்பட்டியைத் திறக்கவும்",
"searchPlaceholder": "URL ஐத் தேடவும் அல்லது தட்டச்சு செய்யவும்" "searchPlaceholder": "URL ஐத் தேடவும் அல்லது தட்டச்சு செய்யவும்",
"setShortcutKeys": "குறுக்குவழி விசைகளை அமைக்கவும்",
"mobileView": "மொபைல் பார்வை"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "ఈ పేజీ ఆటోమేటిక్ పంపడానికి మద్దతు ఇవ్వదు. దయచేసి సందేశాన్ని కాపీ చేసి మానవీయంగా పంపండి." "notSupported": "ఈ పేజీ ఆటోమేటిక్ పంపడానికి మద్దతు ఇవ్వదు. దయచేసి సందేశాన్ని కాపీ చేసి మానవీయంగా పంపండి."
}, },
"openSidebar": "సైడ్‌బార్ తెరవండి", "openSidebar": "సైడ్‌బార్ తెరవండి",
"searchPlaceholder": "URLని శోధించండి లేదా టైప్ చేయండి" "searchPlaceholder": "URLని శోధించండి లేదా టైప్ చేయండి",
"setShortcutKeys": "షార్ట్‌కట్ కీలను సెట్ చేయండి",
"mobileView": "మొబైల్ వీక్షణ"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "หน้านี้ไม่รองรับการส่งอัตโนมัติ กรุณาคัดลอกข้อความและส่งด้วยตนเอง" "notSupported": "หน้านี้ไม่รองรับการส่งอัตโนมัติ กรุณาคัดลอกข้อความและส่งด้วยตนเอง"
}, },
"openSidebar": "เปิดแถบด้านข้าง", "openSidebar": "เปิดแถบด้านข้าง",
"searchPlaceholder": "ค้นหาหรือพิมพ์ URL" "searchPlaceholder": "ค้นหาหรือพิมพ์ URL",
"setShortcutKeys": "ตั้งค่าปุ่มลัด",
"mobileView": "มุมมองมือถือ"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Bu sayfa otomatik göndermeyi desteklemez. Lütfen mesajı kopyalayın ve manuel olarak gönderin." "notSupported": "Bu sayfa otomatik göndermeyi desteklemez. Lütfen mesajı kopyalayın ve manuel olarak gönderin."
}, },
"openSidebar": "Kenar çubuğunu aç", "openSidebar": "Kenar çubuğunu aç",
"searchPlaceholder": "Bir URL arayın veya yazın" "searchPlaceholder": "Bir URL arayın veya yazın",
"setShortcutKeys": "Kısayol tuşlarını ayarla",
"mobileView": "Mobil görünüm"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Ця сторінка не підтримує автоматичне надсилання. Будь ласка, скопіюйте повідомлення та надішліть його вручну." "notSupported": "Ця сторінка не підтримує автоматичне надсилання. Будь ласка, скопіюйте повідомлення та надішліть його вручну."
}, },
"openSidebar": "Відкрийте бічну панель", "openSidebar": "Відкрийте бічну панель",
"searchPlaceholder": "Знайдіть або введіть URL-адресу" "searchPlaceholder": "Знайдіть або введіть URL-адресу",
"setShortcutKeys": "Встановити комбінації клавіш",
"mobileView": "Мобільний вид"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "Trang này không hỗ trợ gửi tự động. Vui lòng sao chép tin nhắn và gửi thủ công." "notSupported": "Trang này không hỗ trợ gửi tự động. Vui lòng sao chép tin nhắn và gửi thủ công."
}, },
"openSidebar": "Mở thanh bên", "openSidebar": "Mở thanh bên",
"searchPlaceholder": "Tìm kiếm hoặc nhập URL" "searchPlaceholder": "Tìm kiếm hoặc nhập URL",
"setShortcutKeys": "Đặt phím tắt",
"mobileView": "Chế độ xem trên thiết bị di động"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "此页面不支持自动发送,请复制消息发送" "notSupported": "此页面不支持自动发送,请复制消息发送"
}, },
"openSidebar": "打开侧边栏", "openSidebar": "打开侧边栏",
"searchPlaceholder": "搜索或输入 URL" "searchPlaceholder": "搜索或输入 URL",
"setShortcutKeys": "设置快捷键",
"mobileView": "移动端视图"
} }
\ No newline at end of file
...@@ -44,5 +44,7 @@ ...@@ -44,5 +44,7 @@
"notSupported": "此頁面不支持自動發送。請複制消息並手動發送。" "notSupported": "此頁面不支持自動發送。請複制消息並手動發送。"
}, },
"openSidebar": "打開側邊欄", "openSidebar": "打開側邊欄",
"searchPlaceholder": "搜尋或輸入 URL" "searchPlaceholder": "搜尋或輸入 URL",
"setShortcutKeys": "設定快速鍵",
"mobileView": "行動端視圖"
} }
\ No newline at end of file
...@@ -24,7 +24,7 @@ export const defaultSidebarPath = "sidebar.html" ...@@ -24,7 +24,7 @@ export const defaultSidebarPath = "sidebar.html"
const manifest = { const manifest = {
// maximum of 45 characters // maximum of 45 characters
name: "__MSG_name__", name: "__MSG_name__",
version: "1.2.8", version: "1.2.9",
// edge 12 characters // edge 12 characters
// short_name: "__MSG_short_name__", // short_name: "__MSG_short_name__",
// no more than 132 characters // no more than 132 characters
......
<script setup lang="ts"> <script setup lang="ts">
import ContentSidebarAddon from "@/components/sidebar/ContentSidebarAddon.vue" import Webview from "@/components/Webview.vue"
</script> </script>
<template> <template>
<!-- <ContentSidebarAddon /> --> <!-- <div class="w-screen h-screen">
<Webview url="https://github.com" />
</div> -->
</template> </template>
<style scoped></style> <style scoped></style>
...@@ -49,18 +49,25 @@ const host = computed({ ...@@ -49,18 +49,25 @@ const host = computed({
watch( watch(
() => pipWindow.id, () => pipWindow.id,
async (id) => { async (id) => {
console.log("pip window tabs: ", id)
if (id) { if (id) {
const tabs = await chrome.tabs.query({ windowId: id })
if (tabs && tabs.length == 1) {
pipWindow.tab = tabs[0]
}
const win = await chrome.windows.get(id) const win = await chrome.windows.get(id)
pipWindow.windowsWindow = win pipWindow.windowsWindow = win
return } else {
pipWindow.windowsWindow = null
}
} }
)
watch(
() => pipWindow.tabId,
async (tabId) => {
if (tabId) {
const tab = await chrome.tabs.get(tabId)
pipWindow.tab = tab
} else {
pipWindow.tab = null pipWindow.tab = null
} }
}
) )
onMounted(() => { onMounted(() => {
...@@ -73,16 +80,19 @@ onMounted(() => { ...@@ -73,16 +80,19 @@ onMounted(() => {
}) })
}) })
getLocal({ pipWindowId: null, popularSites: config.data.popularSites }).then( getLocal({
({ pipWindowId: id, popularSites }) => { pipWindow: null as null | { windowId: number; tabId: number; icon: string },
if (id) { popularSites: config.data.popularSites,
pipWindow.id = id }).then(({ pipWindow: pip, popularSites }) => {
if (pip) {
pipWindow.id = pip.windowId
pipWindow.tabId = pip.tabId
pipWindow.icon = pip.icon
} }
if (Array.isArray(popularSites)) { if (Array.isArray(popularSites)) {
popularItems.splice(0, popularItems.length, ...popularSites) popularItems.splice(0, popularItems.length, ...popularSites)
} }
} })
)
chrome.storage.local.onChanged.addListener(handleLocalChange) chrome.storage.local.onChanged.addListener(handleLocalChange)
window.addEventListener("keydown", handleKeydown) window.addEventListener("keydown", handleKeydown)
...@@ -98,7 +108,7 @@ onUnmounted(() => { ...@@ -98,7 +108,7 @@ onUnmounted(() => {
function handleKeydown(e: KeyboardEvent) { function handleKeydown(e: KeyboardEvent) {
if (e.code == "KeyP" && (e.ctrlKey || e.metaKey)) { if (e.code == "KeyP" && (e.ctrlKey || e.metaKey)) {
e.preventDefault() e.preventDefault()
handleWriteHtml() handlePipPopup()
} }
if (e.code == "Backslash" && (e.ctrlKey || e.metaKey)) { if (e.code == "Backslash" && (e.ctrlKey || e.metaKey)) {
e.preventDefault() e.preventDefault()
...@@ -125,12 +135,13 @@ function hanldeKeyup(e: KeyboardEvent) { ...@@ -125,12 +135,13 @@ function hanldeKeyup(e: KeyboardEvent) {
const handleLocalChange = (changes: { const handleLocalChange = (changes: {
[key: string]: chrome.storage.StorageChange [key: string]: chrome.storage.StorageChange
}) => { }) => {
if (changes.pipWindowId) { if (changes.pipWindow) {
pipWindow.id = changes.pipWindowId.newValue pipWindow.id = changes.pipWindow.newValue.windowId
pipWindow.tabId = changes.pipWindow.newValue.tabId
} }
} }
async function handleWriteHtml() { async function handlePipPopup() {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true }) const tabs = await chrome.tabs.query({ active: true, currentWindow: true })
const tab = tabs[0] const tab = tabs[0]
if (tab) { if (tab) {
...@@ -142,6 +153,8 @@ async function handleWriteHtml() { ...@@ -142,6 +153,8 @@ async function handleWriteHtml() {
}, },
}) })
} }
window.close()
} }
async function openSidebar(url = "") { async function openSidebar(url = "") {
...@@ -270,11 +283,11 @@ function showChatDocs() { ...@@ -270,11 +283,11 @@ function showChatDocs() {
]" ]"
> >
<button <button
v-if="!pipWindow.tab" v-if="!(pipWindow.tab && pipWindow.windowsWindow)"
class="hover:bg-background-mute bg-background-soft disabled:bg-background-soft disabled:opacity-50 disabled:cursor-not-allowed" class="hover:bg-background-mute bg-background-soft disabled:bg-background-soft disabled:opacity-50 disabled:cursor-not-allowed"
:disabled="!avaiable" :disabled="!avaiable"
:title="t('openInPip')" :title="t('openInPip')"
@click="handleWriteHtml" @click="handlePipPopup"
> >
<IconAmpStories class="size-8 shrink-0" /> <IconAmpStories class="size-8 shrink-0" />
<div class="flex items-center justify-between flex-1 w-2/3 gap-2"> <div class="flex items-center justify-between flex-1 w-2/3 gap-2">
......
...@@ -19,6 +19,7 @@ import IconNavigateNext from "@/components/icons/IconNavigateNext.vue" ...@@ -19,6 +19,7 @@ import IconNavigateNext from "@/components/icons/IconNavigateNext.vue"
import IconRefresh from "@/components/icons/IconRefresh.vue" import IconRefresh from "@/components/icons/IconRefresh.vue"
import IconHome from "@/components/icons/IconHome.vue" import IconHome from "@/components/icons/IconHome.vue"
import IconPhone from "@/components/icons/IconPhone.vue" import IconPhone from "@/components/icons/IconPhone.vue"
import IconHide from "@/components/icons/IconHide.vue"
import { handleImgError } from "@/utils/dom" import { handleImgError } from "@/utils/dom"
import { FrameMessageType } from "@/types" import { FrameMessageType } from "@/types"
import { homeUrl } from "@/utils/const" import { homeUrl } from "@/utils/const"
...@@ -52,7 +53,7 @@ const webviewsAttr = computed(() => { ...@@ -52,7 +53,7 @@ const webviewsAttr = computed(() => {
}) })
}) })
const active = ref(-1) const active = ref(-1)
const pagesInfo = reactive<{ [index: number]: PageInfo }>({}) const pagesInfo = reactive<(PageInfo | null)[]>([])
const hostUA = reactive<Record<string, number>>(config.data.embedView.hostUA) const hostUA = reactive<Record<string, number>>(config.data.embedView.hostUA)
const isPointerIn = ref(false) const isPointerIn = ref(false)
...@@ -155,12 +156,8 @@ onUnmounted(() => { ...@@ -155,12 +156,8 @@ onUnmounted(() => {
async function handleMessage(message: any) { async function handleMessage(message: any) {
switch (message.type) { switch (message.type) {
case MessageType.openInSidebar: case MessageType.openInSidebar:
if (currentTab.tabId == 0) {
const current = await chrome.tabs.getCurrent()
currentTab.tabId = current?.id || -1
}
if (message.tabId == currentTab.tabId) {
const url = message.url const url = message.url
if (message.tabId == currentTab.tabId && !isProtectedUrl(url)) {
go(url) go(url)
} }
break break
...@@ -231,7 +228,7 @@ function reload() { ...@@ -231,7 +228,7 @@ function reload() {
function closeWebview(index: number) { function closeWebview(index: number) {
pages.splice(index, 1) pages.splice(index, 1)
delete pagesInfo[index] pagesInfo.splice(index, 1)
if (active.value > pages.length - 1) { if (active.value > pages.length - 1) {
active.value = pages.length - 1 active.value = pages.length - 1
} }
...@@ -332,8 +329,9 @@ function handlePointerLeave() { ...@@ -332,8 +329,9 @@ function handlePointerLeave() {
<a <a
@click="(e) => (e.preventDefault(), (active = -1))" @click="(e) => (e.preventDefault(), (active = -1))"
:href="homeUrl" :href="homeUrl"
title="Anything Copilot"
:class="[ :class="[
'group rounded-lg relative box-border border', 'group rounded-full relative box-border border',
active === -1 active === -1
? 'bg-primary/10 border-primary-500' ? 'bg-primary/10 border-primary-500'
: 'border-transparent hover:bg-background-mute bg-background-soft', : 'border-transparent hover:bg-background-mute bg-background-soft',
...@@ -352,16 +350,17 @@ function handlePointerLeave() { ...@@ -352,16 +350,17 @@ function handlePointerLeave() {
<a <a
v-for="(page, i) of pages" v-for="(page, i) of pages"
@click="(e) => (e.preventDefault(), (active = i))" @click="(e) => (e.preventDefault(), (active = i))"
:href="page.url" :href="pagesInfo[i]?.url"
:title="pagesInfo[i]?.title"
:class="[ :class="[
'group rounded-lg relative box-border border', 'group rounded-full relative box-border border',
active === i active === i
? 'bg-primary/10 border-primary-500' ? 'bg-primary/10 border-primary-500'
: 'border-transparent hover:bg-background-mute bg-background-soft', : 'border-transparent hover:bg-background-mute bg-background-soft',
]" ]"
> >
<img <img
class="size-5 scale-90 pointer-events-none" class="size-4 pointer-events-none"
loading="lazy" loading="lazy"
:src="pagesInfo[i]?.icon || globeImg" :src="pagesInfo[i]?.icon || globeImg"
:data-fallback="globeImg" :data-fallback="globeImg"
...@@ -401,6 +400,7 @@ function handlePointerLeave() { ...@@ -401,6 +400,7 @@ function handlePointerLeave() {
</button> --> </button> -->
<button <button
@click="reload()" @click="reload()"
:title="t('refresh')"
class="group hover:bg-background-soft rounded-full" class="group hover:bg-background-soft rounded-full"
> >
<IconRefresh <IconRefresh
...@@ -409,6 +409,7 @@ function handlePointerLeave() { ...@@ -409,6 +409,7 @@ function handlePointerLeave() {
</button> </button>
<button <button
@click="toggleMobileUA" @click="toggleMobileUA"
:title="t('mobileView')"
:class="[ :class="[
'group hover:bg-background-soft rounded-full transition ease-in-out delay-200', 'group hover:bg-background-soft rounded-full transition ease-in-out delay-200',
{ 'bg-background-soft text-primary-500 ': isMobileUA }, { 'bg-background-soft text-primary-500 ': isMobileUA },
...@@ -420,15 +421,17 @@ function handlePointerLeave() { ...@@ -420,15 +421,17 @@ function handlePointerLeave() {
<button <button
v-if="mode == 'content'" v-if="mode == 'content'"
@click="collapseSidebar()" @click="collapseSidebar()"
:title="t('minimize')"
class="group hover:bg-background-soft rounded-full" class="group hover:bg-background-soft rounded-full"
> >
<IconSplitscreenRight <IconHide
class="size-5 scale-95 group-active:scale-90 transition-transform" class="size-5 scale-95 group-active:scale-90 transition-transform"
/> />
</button> </button>
<button <button
v-if="mode == 'content'" v-if="mode == 'content'"
@click="closeSidebar()" @click="closeSidebar()"
:title="t('close')"
class="group hover:bg-background-soft rounded-full" class="group hover:bg-background-soft rounded-full"
> >
<IconClose class="size-5 group-active:scale-90 transition-transform" /> <IconClose class="size-5 group-active:scale-90 transition-transform" />
......
import "@/assets/main.css" import "@/assets/main.css"
import { createApp } from "vue" import { createApp } from "vue"
import "@/content/index" import "@/content/index"
import "@/content/main"
// import "@/pages/popup" // import "@/pages/popup"
import Dev from "./Dev.vue" import Dev from "./Dev.vue"
......
...@@ -6,10 +6,10 @@ import TurndownService from "turndown" ...@@ -6,10 +6,10 @@ import TurndownService from "turndown"
import { MessageType, ServiceFunc, type ParseDocOptions } from "@/types" import { MessageType, ServiceFunc, type ParseDocOptions } from "@/types"
import { Tiktoken } from "tiktoken/lite" import { Tiktoken } from "tiktoken/lite"
import cl100k_base from "tiktoken/encoders/cl100k_base.json" import cl100k_base from "tiktoken/encoders/cl100k_base.json"
import { contentInvoke } from "@/utils/invoke"
pdfjs.GlobalWorkerOptions.workerSrc = "/js/pdf.worker.js" pdfjs.GlobalWorkerOptions.workerSrc = "/js/pdf.worker.js"
async function parsePdf(file: File) { async function parsePdf(file: File) {
const buffer = await file.arrayBuffer() const buffer = await file.arrayBuffer()
const task = pdfjs.getDocument(buffer) const task = pdfjs.getDocument(buffer)
...@@ -102,34 +102,27 @@ async function handleMessage( ...@@ -102,34 +102,27 @@ async function handleMessage(
message: any, message: any,
sender: chrome.runtime.MessageSender sender: chrome.runtime.MessageSender
) { ) {
if (message?.type === MessageType.toOffscreen) {
console.log("offscreen message: ", message, sender) console.log("offscreen message: ", message, sender)
const { func, args } = message if (message?.type === MessageType.invokeRequest) {
let taskPromise: Promise<any> | null = null const result = await contentInvoke.handleReqMsg(message)
switch (func) { if (result) {
case ServiceFunc.parseDoc:
taskPromise = parseDoc(...(args as [any]))
break
case ServiceFunc.calcTokens:
taskPromise = calcTokens(...(args as [any]))
break
case ServiceFunc.tokenSlice:
taskPromise = tokenSlice(...(args as [any, any]))
break
}
if (taskPromise) {
const result = await taskPromise
chrome.runtime.sendMessage({ chrome.runtime.sendMessage({
type: MessageType.fromOffscreen, type: MessageType.forwardToTab,
key: message.key, tabId: sender.tab?.id,
task: message.task, message: {
payload: result, type: MessageType.invokeResponse,
...result,
},
}) })
} }
} }
} }
contentInvoke
.register(ServiceFunc.parseDoc, parseDoc)
.register(ServiceFunc.calcTokens, calcTokens)
.register(ServiceFunc.tokenSlice, tokenSlice)
chrome.runtime.onMessage.addListener(handleMessage) chrome.runtime.onMessage.addListener(handleMessage)
// for testing purposes // for testing purposes
......
...@@ -3,8 +3,18 @@ import "@/assets/main.css" ...@@ -3,8 +3,18 @@ import "@/assets/main.css"
import { createApp } from "vue" import { createApp } from "vue"
import Popup from "./Popup.vue" import Popup from "./Popup.vue"
import { i18n } from "@/utils/i18n" import { i18n } from "@/utils/i18n"
import { MessageType } from "@/types"
import { contentInvoke } from "@/utils/invoke"
const app = createApp(Popup) const app = createApp(Popup)
app.use(i18n) app.use(i18n)
app.mount("#app") app.mount("#app")
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
switch (message.type) {
case MessageType.invokeResponse:
contentInvoke.handleResMsg(message)
break
}
})
...@@ -5,4 +5,6 @@ export const pipWindow = reactive({ ...@@ -5,4 +5,6 @@ export const pipWindow = reactive({
window: null as Window | null, window: null as Window | null,
windowsWindow: null as chrome.windows.Window | null, windowsWindow: null as chrome.windows.Window | null,
tab: null as chrome.tabs.Tab | null, tab: null as chrome.tabs.Tab | null,
tabId: 0,
icon: "" as string,
}) })
...@@ -6,13 +6,13 @@ export enum MessageType { ...@@ -6,13 +6,13 @@ export enum MessageType {
bgPipLaunch = "bg-pip-launch", bgPipLaunch = "bg-pip-launch",
pipLaunch = "pip-launch", pipLaunch = "pip-launch",
contentMounted = "content-mount", contentMounted = "content-mount",
getPipWinInfo = "get-pip-win-info", // getPipWinInfo = "get-pip-win-info",
pipWinInfo = "pip-win-info", // pipWinInfo = "pip-win-info",
updateWindow = "update-window", updateWindow = "update-window",
removeWindow = "remove-window", removeWindow = "remove-window",
setupOffscreenDocument = "setup-offscreen-document", forwardToTab = "forward-to-tab",
toOffscreen = "to-offscreen", // toOffscreen = "to-offscreen",
fromOffscreen = "from-offscreen", // fromOffscreen = "from-offscreen",
invokeRequest = "invoke-request", invokeRequest = "invoke-request",
invokeResponse = "invoke-Response", invokeResponse = "invoke-Response",
showChatDocs = "show-chat-docs", showChatDocs = "show-chat-docs",
...@@ -23,9 +23,15 @@ export enum MessageType { ...@@ -23,9 +23,15 @@ export enum MessageType {
} }
export enum ServiceFunc { export enum ServiceFunc {
setupOffscreen = "setup-offscreen",
parseDoc = "parse-doc", parseDoc = "parse-doc",
calcTokens = "calc-tokens", calcTokens = "calc-tokens",
tokenSlice = "token-slice", tokenSlice = "token-slice",
getAllCommands = "get-all-commands",
createTab = "create-tab",
toggleMinimize = "toggle-minimize",
getPipWindow = "get-pip-window",
getMyTab = "get-my-tab",
} }
export type ParseDocOptions = { export type ParseDocOptions = {
...@@ -51,7 +57,6 @@ export enum ContentEventType { ...@@ -51,7 +57,6 @@ export enum ContentEventType {
export enum FrameMessageType { export enum FrameMessageType {
frameReady = "anything-copilot_frame-ready", frameReady = "anything-copilot_frame-ready",
webviewRun = "anything-copilot_webview-run",
escapeLoad = "anything-copilot_escape-load", escapeLoad = "anything-copilot_escape-load",
pageInfo = "anything-copilot_page-info", pageInfo = "anything-copilot_page-info",
collapseSidebar = "anything-copilot_collapse-sidebar", collapseSidebar = "anything-copilot_collapse-sidebar",
......
...@@ -2,4 +2,3 @@ export const chatDocPrompt = `Here is some relevant reference information. Pleas ...@@ -2,4 +2,3 @@ export const chatDocPrompt = `Here is some relevant reference information. Pleas
export const homeUrl = 'https://ziziyi.com/copilot' export const homeUrl = 'https://ziziyi.com/copilot'
export const feedbackUrl = 'https://tawk.to/anythingcopilot' export const feedbackUrl = 'https://tawk.to/anythingcopilot'
...@@ -327,3 +327,22 @@ export function isProtectedUrl(url: string) { ...@@ -327,3 +327,22 @@ export function isProtectedUrl(url: string) {
return true return true
} }
export async function getPipWindow({
windowId,
width,
height,
}: {
windowId?: number
width?: number
height?: number
}) {
if (windowId) {
const win = await chrome.windows.get(windowId)
return win
}
const windows = await chrome.windows.getAll({})
const win = windows.find((w) => w.width === width && w.height === height)
return win
}
...@@ -6,9 +6,9 @@ import type ZhMessage from "@/locales/zh-CN.json" ...@@ -6,9 +6,9 @@ import type ZhMessage from "@/locales/zh-CN.json"
type MessageSchema = typeof EnMessage & typeof ZhMessage type MessageSchema = typeof EnMessage & typeof ZhMessage
export function getLocale() { export function getLocale() {
if (__DEV__) { // if (__DEV__) {
return "zh-CN" // return "zh-CN"
} // }
const language = chrome.i18n.getUILanguage() const language = chrome.i18n.getUILanguage()
let code = language let code = language
......
import { MessageType, ServiceFunc, type ParseDocOptions } from "@/types" import { MessageType, ServiceFunc, type ParseDocOptions } from "@/types"
import Invoke from "./Invoke" import Invoke, { type InvokeReq } from "./Invoke"
class ContentInvoke extends Invoke { class ContentInvoke extends Invoke {
public async send(req: any) { public async send(req: InvokeReq & { tabId?: number }) {
const key = this.key const key = this.key
const response = await chrome.runtime.sendMessage({
if (req.tabId) {
chrome.tabs.sendMessage(req.tabId, {
type: MessageType.invokeRequest,
key,
...req,
})
} else {
chrome.runtime.sendMessage({
type: MessageType.invokeRequest, type: MessageType.invokeRequest,
key, key,
...req, ...req,
}) })
}
return { key, response } return { key }
} }
public handleResMsg(message: any) { public handleResMsg(message: any) {
if (message?.type === MessageType.invokeResponse) { if (message?.type === MessageType.invokeResponse) {
const { key, success, payload } = message const { key, success, value } = message
this.setReturnValue(key, success, payload) this.setReturnValue(key, success, value)
}
} }
public setupOffscreen() {
return this.invoke({
func: ServiceFunc.setupOffscreen,
args: [],
})
} }
public parseDoc(options: ParseDocOptions): Promise<string[]> { public parseDoc(options: ParseDocOptions): Promise<string[]> {
...@@ -40,6 +56,22 @@ class ContentInvoke extends Invoke { ...@@ -40,6 +56,22 @@ class ContentInvoke extends Invoke {
args: [text, length], args: [text, length],
}) })
} }
public getAllCommands(): Promise<chrome.commands.Command[]> {
return this.invoke({
func: ServiceFunc.getAllCommands,
args: [],
})
}
public createTab(
properties: chrome.tabs.CreateProperties
): Promise<chrome.tabs.Tab> {
return this.invoke({
func: ServiceFunc.createTab,
args: [properties],
})
}
} }
const contentInvoke = new ContentInvoke("content") const contentInvoke = new ContentInvoke("content")
......
...@@ -3,29 +3,33 @@ type CallbackItem = { ...@@ -3,29 +3,33 @@ type CallbackItem = {
reject: (e: any) => void reject: (e: any) => void
} }
interface InvokeReq { export interface InvokeReq {
func: string func: string
args: any[] args: any[]
timeout?: number timeout?: number
key?: string
[key: string]: any
} }
interface InvokeRes { export interface InvokeRes {
key: string key: string
success: boolean success: boolean
value: any value: any
} }
abstract class Invoke { export abstract class Invoke {
private pendingCallback: Record<string, CallbackItem> private pendingCallback: Record<string, CallbackItem>
private count: number private count: number
private name: string private name: string
private uniqueId: string private uniqueId: string
private services: Map<string, Function>
constructor(name: string) { constructor(name: string) {
this.name = name this.name = name
this.uniqueId = `${Math.round(Math.random() * 1e6)}` this.uniqueId = `${Math.round(Math.random() * 1e6)}`
this.pendingCallback = {} this.pendingCallback = {}
this.count = 0 this.count = 0
this.services = new Map()
} }
protected get key() { protected get key() {
...@@ -64,15 +68,48 @@ abstract class Invoke { ...@@ -64,15 +68,48 @@ abstract class Invoke {
} }
} }
abstract send(req: InvokeReq): Promise<{ key: string; response: any }> abstract send(req: InvokeReq): Promise<{ key: string }>
abstract handleResMsg(message: InvokeRes): void public handleResMsg(message: InvokeRes) {
const { key, success, value } = message
this.setReturnValue(key, success, value)
}
public async handleReqMsg(message: InvokeReq) {
const { key, func, args, timeout } = message
let result = null
let error = null
try {
const service = this.services.get(func)
if (service) {
result = await service(...args)
} else {
error = `unknown service: ${func}`
console.warn(`unknown service: ${func}`)
return null
}
} catch (err) {
console.error("invoke error: ", err)
error = err
}
return { key, success: !error, value: !error ? result : error }
}
public async invoke<T = any>(req: InvokeReq): Promise<T> { public async invoke<T = any>(req: InvokeReq): Promise<T> {
const { key } = await this.send(req) const { key } = await this.send(req)
req.timeout = req.timeout || 20000 req.timeout = req.timeout || 20000
return this.getReturnValue(key, req) return this.getReturnValue(key, req)
} }
public register(func: string, service: Function) {
this.services.set(func, service)
return this
}
public unregister(func: string) {
this.services.delete(func)
return this
}
} }
export default Invoke export default Invoke
export { Invoke }
...@@ -18,13 +18,6 @@ class WebviewInvoke extends Invoke { ...@@ -18,13 +18,6 @@ class WebviewInvoke extends Invoke {
win?.postMessage({ type: FrameMessageType.invokeRequest, key, ...req }, "*") win?.postMessage({ type: FrameMessageType.invokeRequest, key, ...req }, "*")
return { key, response: null } return { key, response: null }
} }
public handleResMsg(message: any) {
if (message?.type === FrameMessageType.invokeResponse) {
const { key, success, payload } = message
this.setReturnValue(key, success, payload)
}
}
} }
export { WebviewInvoke } export { WebviewInvoke }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment