Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
N
n8n_ai_assistant
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Vũ Hoàng Anh
n8n_ai_assistant
Commits
31d553e6
Commit
31d553e6
authored
Mar 09, 2024
by
Domi
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: content sidebar
parent
1d61640e
Changes
82
Hide whitespace changes
Inline
Side-by-side
Showing
82 changed files
with
808 additions
and
241 deletions
+808
-241
dev.html
dev.html
+5
-2
esbuild.mjs
esbuild.mjs
+0
-1
config.json
src/assets/config.json
+3
-3
index.ts
src/bg/index.ts
+20
-9
sidebar.ts
src/bg/sidebar.ts
+29
-0
SiteButton.vue
src/components/SiteButton.vue
+65
-0
SiteButtonItem.vue
src/components/SiteButtonItem.vue
+0
-21
Template.vue
src/components/Template.vue
+3
-1
Webview.vue
src/components/Webview.vue
+6
-18
ChatDocsAddon.vue
src/components/chatdocs/ChatDocsAddon.vue
+2
-2
IconDockToLeft.vue
src/components/icons/IconDockToLeft.vue
+13
-0
ContentSidebar.vue
src/components/sidebar/ContentSidebar.vue
+149
-0
ContentSidebarAddon.vue
src/components/sidebar/ContentSidebarAddon.vue
+41
-0
PageScrollbar.vue
src/components/sidebar/PageScrollbar.vue
+82
-0
App.vue
src/content/App.vue
+11
-2
frame.ts
src/content/frame.ts
+0
-5
index.ts
src/content/index.ts
+33
-19
ui.ts
src/content/ui.ts
+0
-1
am.json
src/locales/am.json
+2
-1
ar.json
src/locales/ar.json
+2
-1
bg.json
src/locales/bg.json
+2
-1
bn.json
src/locales/bn.json
+2
-1
ca.json
src/locales/ca.json
+2
-1
cs.json
src/locales/cs.json
+2
-1
da.json
src/locales/da.json
+2
-1
de.json
src/locales/de.json
+2
-1
el.json
src/locales/el.json
+2
-1
en.json
src/locales/en.json
+2
-1
es-419.json
src/locales/es-419.json
+2
-1
es.json
src/locales/es.json
+2
-1
et.json
src/locales/et.json
+2
-1
fa.json
src/locales/fa.json
+2
-1
fi.json
src/locales/fi.json
+2
-1
fil.json
src/locales/fil.json
+2
-1
fr.json
src/locales/fr.json
+2
-1
gu.json
src/locales/gu.json
+2
-1
he.json
src/locales/he.json
+2
-1
hi.json
src/locales/hi.json
+2
-1
hr.json
src/locales/hr.json
+2
-1
hu.json
src/locales/hu.json
+2
-1
id.json
src/locales/id.json
+2
-1
it.json
src/locales/it.json
+2
-1
ja.json
src/locales/ja.json
+2
-1
kn.json
src/locales/kn.json
+2
-1
ko.json
src/locales/ko.json
+2
-1
lt.json
src/locales/lt.json
+2
-1
lv.json
src/locales/lv.json
+2
-1
ml.json
src/locales/ml.json
+2
-1
mr.json
src/locales/mr.json
+2
-1
ms.json
src/locales/ms.json
+2
-1
nl.json
src/locales/nl.json
+2
-1
no.json
src/locales/no.json
+2
-1
pl.json
src/locales/pl.json
+2
-1
pt-BR.json
src/locales/pt-BR.json
+2
-1
pt-PT.json
src/locales/pt-PT.json
+2
-1
ro.json
src/locales/ro.json
+2
-1
ru.json
src/locales/ru.json
+2
-1
sk.json
src/locales/sk.json
+2
-1
sl.json
src/locales/sl.json
+2
-1
sr.json
src/locales/sr.json
+2
-1
sv.json
src/locales/sv.json
+2
-1
sw.json
src/locales/sw.json
+2
-1
ta.json
src/locales/ta.json
+2
-1
te.json
src/locales/te.json
+2
-1
th.json
src/locales/th.json
+2
-1
tr.json
src/locales/tr.json
+2
-1
uk.json
src/locales/uk.json
+2
-1
vi.json
src/locales/vi.json
+2
-1
zh-CN.json
src/locales/zh-CN.json
+2
-1
zh-TW.json
src/locales/zh-TW.json
+2
-1
manifest.ts
src/manifest.ts
+2
-7
Dev.vue
src/pages/Dev.vue
+9
-0
Popup.vue
src/pages/Popup.vue
+64
-48
Sidebar.vue
src/pages/Sidebar.vue
+106
-43
dev.ts
src/pages/dev.ts
+12
-2
index.ts
src/store/index.ts
+6
-0
chrome.ts
src/types/chrome.ts
+4
-2
index.ts
src/types/index.ts
+5
-2
dom.ts
src/utils/dom.ts
+6
-0
ext.ts
src/utils/ext.ts
+25
-0
utils.ts
src/utils/utils.ts
+2
-1
vite.config.ts
vite.config.ts
+1
-0
No files found.
dev.html
View file @
31d553e6
...
@@ -4,11 +4,14 @@
...
@@ -4,11 +4,14 @@
<meta
charset=
"UTF-8"
/>
<meta
charset=
"UTF-8"
/>
<link
rel=
"icon"
href=
"/favicon.ico"
/>
<link
rel=
"icon"
href=
"/favicon.ico"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
/>
<title>
Anything Copilot
</title>
<title>
Anything Copilot DEV
</title>
<base
href=
"http://localhost:3000"
/>
</head>
</head>
<body>
<body>
<div
id=
"app"
></div>
<div
id=
"app"
></div>
<div>
<div
class=
"h-screen"
></div>
<div
class=
"h-screen"
></div>
</div>
<script
type=
"module"
src=
"./src/pages/dev.ts"
></script>
<script
type=
"module"
src=
"./src/pages/dev.ts"
></script>
</body>
</body>
</html>
</html>
esbuild.mjs
View file @
31d553e6
...
@@ -12,7 +12,6 @@ const ctx = await esbuild.context({
...
@@ -12,7 +12,6 @@ const ctx = await esbuild.context({
entryPoints
:
{
entryPoints
:
{
// bg: "./src/bg/index.ts",
// bg: "./src/bg/index.ts",
"js/content-main"
:
"./src/content/main.ts"
,
"js/content-main"
:
"./src/content/main.ts"
,
"js/content-frame"
:
"./src/content/frame.ts"
,
"js/pdf.worker"
:
"./src/assets/pdf.worker.js"
,
"js/pdf.worker"
:
"./src/assets/pdf.worker.js"
,
},
},
bundle
:
true
,
bundle
:
true
,
...
...
src/assets/config.json
View file @
31d553e6
...
@@ -98,9 +98,9 @@
...
@@ -98,9 +98,9 @@
"icon"
:
"https://r2.ziziyi.com/copilot/gemini.svg"
"icon"
:
"https://r2.ziziyi.com/copilot/gemini.svg"
},
},
{
{
"url"
:
"https://
claude.ai
/"
,
"url"
:
"https://
runwayml.com/ai-tools/text-to-image
/"
,
"title"
:
"
Claud
e"
,
"title"
:
"
Text to Imag
e"
,
"icon"
:
"https://r2.ziziyi.com/copilot/
claude-ai.sv
g"
"icon"
:
"https://r2.ziziyi.com/copilot/
runway.pn
g"
}
}
]
]
}
}
...
...
src/bg/index.ts
View file @
31d553e6
...
@@ -6,6 +6,11 @@ import {
...
@@ -6,6 +6,11 @@ import {
}
from
"@/types"
}
from
"@/types"
import
{
waitMessage
,
tabUpdated
,
getLocal
}
from
"@/utils/ext"
import
{
waitMessage
,
tabUpdated
,
getLocal
}
from
"@/utils/ext"
import
{
offscreen
}
from
"./offscreen"
import
{
offscreen
}
from
"./offscreen"
import
{
registerContentSidebar
,
unregisterContentSidebar
,
handleContentMounted
,
}
from
"./sidebar"
import
config
from
"@/assets/config.json"
import
config
from
"@/assets/config.json"
import
{
allFrameScript
,
mainContentScript
}
from
"@/manifest"
import
{
allFrameScript
,
mainContentScript
}
from
"@/manifest"
...
@@ -22,6 +27,11 @@ const contentScript = {
...
@@ -22,6 +27,11 @@ const contentScript = {
}
satisfies
chrome
.
scripting
.
RegisteredContentScript
}
satisfies
chrome
.
scripting
.
RegisteredContentScript
chrome
.
scripting
.
registerContentScripts
([
contentScript
,
mainContentScript
])
chrome
.
scripting
.
registerContentScripts
([
contentScript
,
mainContentScript
])
chrome
.
runtime
.
onMessage
.
addListener
(
handleMessage
)
chrome
.
commands
.
onCommand
.
addListener
(
handleCommand
)
chrome
.
runtime
.
onStartup
.
addListener
(()
=>
{
updateConfig
()
})
async
function
openPipBackground
(
url
:
string
)
{
async
function
openPipBackground
(
url
:
string
)
{
const
tab
=
await
chrome
.
tabs
.
create
({
const
tab
=
await
chrome
.
tabs
.
create
({
...
@@ -43,7 +53,7 @@ async function pipLaunch(url: string) {
...
@@ -43,7 +53,7 @@ async function pipLaunch(url: string) {
const
tab
=
await
chrome
.
tabs
.
create
({
url
})
const
tab
=
await
chrome
.
tabs
.
create
({
url
})
await
waitMessage
({
await
waitMessage
({
tabId
:
tab
.
id
!
,
tabId
:
tab
.
id
!
,
type
:
MessageType
.
contentMount
,
type
:
MessageType
.
contentMount
ed
,
})
})
chrome
.
tabs
.
sendMessage
(
tab
.
id
!
,
{
chrome
.
tabs
.
sendMessage
(
tab
.
id
!
,
{
type
:
MessageType
.
pipLaunch
,
type
:
MessageType
.
pipLaunch
,
...
@@ -150,11 +160,18 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender) {
...
@@ -150,11 +160,18 @@ function handleMessage(message: any, sender: chrome.runtime.MessageSender) {
case
MessageType
.
invokeRequest
:
case
MessageType
.
invokeRequest
:
handleInvokeRequest
(
message
,
sender
)
handleInvokeRequest
(
message
,
sender
)
break
break
case
MessageType
.
contentMounted
:
handleContentMounted
(
sender
.
tab
!
.
id
!
)
break
case
MessageType
.
registerContentSidebar
:
registerContentSidebar
({
tabId
:
sender
.
tab
!
.
id
!
,
url
:
message
.
url
})
break
case
MessageType
.
unregisterContentSidebar
:
unregisterContentSidebar
(
sender
.
tab
!
.
id
!
)
break
}
}
}
}
chrome
.
runtime
.
onMessage
.
addListener
(
handleMessage
)
async
function
handleToggleMinimize
()
{
async
function
handleToggleMinimize
()
{
const
{
pipWindowId
}
=
await
chrome
.
storage
.
local
.
get
({
pipWindowId
:
null
})
const
{
pipWindowId
}
=
await
chrome
.
storage
.
local
.
get
({
pipWindowId
:
null
})
if
(
!
pipWindowId
)
return
if
(
!
pipWindowId
)
return
...
@@ -174,8 +191,6 @@ function handleCommand(command: string) {
...
@@ -174,8 +191,6 @@ function handleCommand(command: string) {
}
}
}
}
chrome
.
commands
.
onCommand
.
addListener
(
handleCommand
)
async
function
updateConfig
()
{
async
function
updateConfig
()
{
const
url
=
"https://config.ziziyi.com/anything-copilot"
const
url
=
"https://config.ziziyi.com/anything-copilot"
const
now
=
Date
.
now
()
const
now
=
Date
.
now
()
...
@@ -235,7 +250,3 @@ async function updateConfig() {
...
@@ -235,7 +250,3 @@ async function updateConfig() {
loadCandidates
,
loadCandidates
,
})
})
}
}
chrome
.
runtime
.
onStartup
.
addListener
(()
=>
{
updateConfig
()
})
src/bg/sidebar.ts
0 → 100644
View file @
31d553e6
import
{
MessageType
}
from
"@/types"
type
ContentSidebar
=
{
tabId
:
number
url
?:
string
}
const
contentSidebarMap
=
new
Map
<
number
,
ContentSidebar
>
()
export
function
registerContentSidebar
({
tabId
,
url
}:
ContentSidebar
)
{
contentSidebarMap
.
set
(
tabId
,
{
tabId
,
url
})
}
export
function
unregisterContentSidebar
(
tabId
:
number
)
{
contentSidebarMap
.
delete
(
tabId
)
}
export
async
function
handleContentMounted
(
tabId
:
number
)
{
const
sidebar
=
contentSidebarMap
.
get
(
tabId
)
if
(
sidebar
)
{
await
chrome
.
storage
.
session
.
set
({
sidebarUrls
:
{
content
:
sidebar
.
url
},
})
chrome
.
tabs
.
sendMessage
(
tabId
,
{
type
:
MessageType
.
openContentSidebar
,
url
:
sidebar
.
url
,
})
}
}
src/components/SiteButton.vue
0 → 100644
View file @
31d553e6
<
script
setup
lang=
"ts"
>
import
IconSplitscreenRight
from
"@/components/icons/IconSplitscreenRight.vue"
import
IconAmpStories
from
"@/components/icons/IconAmpStories.vue"
import
IconClose
from
"./icons/IconClose.vue"
defineProps
<
{
icon
:
string
title
:
string
badge
?:
"popup"
|
"sidebar"
|
"remove"
small
?:
boolean
}
>
()
defineEmits
([
"click"
,
"remove"
])
</
script
>
<
template
>
<button
:class=
"[
'group shrink-0 relative flex flex-col items-center justify-self-center rounded-lg p-1 bg-background-soft hover:bg-background-mute',
small ? 'w-[58px]' : 'w-16',
]"
@
click=
"$emit('click')"
>
<span
class=
"size-6 rounded mt-2 mb-1"
:style=
"
{
background: 'center / contain url(' + icon + ')',
}"
>
</span>
<div
class=
"flex flex-col justify-center h-6 my-1"
>
<div
class=
"text-xs max-w-full break-words leading-3 line-clamp-2"
>
{{
title
}}
</div>
</div>
<IconSplitscreenRight
v-if=
"badge === 'sidebar'"
class=
"size-3 absolute top-1 right-1 opacity-0 group-hover:opacity-65"
/>
<IconAmpStories
v-if=
"badge === 'popup'"
class=
"size-3 absolute top-1 right-1 opacity-0 group-hover:opacity-65"
/>
<div
v-if=
"badge === 'remove'"
:class=
"[
'size-4 absolute top-[-4px] right-[-4px] opacity-0 group-hover:opacity-65 rounded-full',
'bg-background-soft hover:text-red-500 flex items-center justify-center',
]"
@
click=
"
(e) =>
{
e.stopPropagation()
e.preventDefault()
$emit('remove')
}
"
>
<IconClose
class=
"size-3"
/>
</div>
</button>
</
template
>
<
style
scoped
></
style
>
src/components/SiteButtonItem.vue
deleted
100644 → 0
View file @
1d61640e
<
script
setup
lang=
"ts"
>
defineProps
<
{
image
?:
string
;
title
:
string
;
}
>
();
</
script
>
<
template
>
<button
class=
"primary-btn w-full flex items-center mt-2 rounded-lg p-2"
>
<span
class=
"w-6 h-6 inline-block mr-2 rounded"
:style=
"
{
background: 'center / contain url(' + image + ')',
}"
>
</span>
<span
class=
"text-base font-bold"
>
{{
title
}}
</span>
</button>
</
template
>
<
style
scoped
></
style
>
src/components/Template.vue
View file @
31d553e6
<
script
setup
lang=
"ts"
></
script
>
<
script
setup
lang=
"ts"
>
import
{}
from
"vue"
</
script
>
<
template
>
<
template
>
<div></div>
<div></div>
...
...
src/components/Webview.vue
View file @
31d553e6
...
@@ -10,6 +10,10 @@ const props = defineProps<{
...
@@ -10,6 +10,10 @@ const props = defineProps<{
url
:
string
url
:
string
}
>
()
}
>
()
const
emit
=
defineEmits
<
{
pageInfo
:
[
pageInfo
:
{
url
:
string
;
title
:
string
;
icon
:
string
}]
}
>
()
const
frame
=
ref
<
HTMLIFrameElement
>
()
const
frame
=
ref
<
HTMLIFrameElement
>
()
const
patchs
=
reactive
(
config
.
data
.
webviewPatchs
)
const
patchs
=
reactive
(
config
.
data
.
webviewPatchs
)
const
loadUrls
=
reactive
(
config
.
data
.
loadCandidates
)
const
loadUrls
=
reactive
(
config
.
data
.
loadCandidates
)
...
@@ -32,23 +36,7 @@ function handleFrameMessage(e: MessageEvent) {
...
@@ -32,23 +36,7 @@ function handleFrameMessage(e: MessageEvent) {
pageInfo
.
title
=
e
.
data
.
title
pageInfo
.
title
=
e
.
data
.
title
pageInfo
.
icon
=
e
.
data
.
icon
pageInfo
.
icon
=
e
.
data
.
icon
getLocal
({
emit
(
"pageInfo"
,
pageInfo
)
sidebarRecentItems
:
[]
as
{
url
:
string
icon
:
string
title
:
string
}[],
}).
then
(({
sidebarRecentItems
})
=>
{
const
index
=
sidebarRecentItems
.
findIndex
(
(
i
)
=>
i
.
url
===
pageInfo
.
url
)
if
(
index
!==
-
1
)
{
sidebarRecentItems
.
splice
(
index
,
1
)
}
sidebarRecentItems
.
unshift
(
pageInfo
)
sidebarRecentItems
.
splice
(
10
)
chrome
.
storage
.
local
.
set
({
sidebarRecentItems
})
})
}
}
break
break
}
}
...
@@ -118,7 +106,7 @@ watch(patch, async (patch) => {
...
@@ -118,7 +106,7 @@ watch(patch, async (patch) => {
pageInfo
.
url
=
""
pageInfo
.
url
=
""
pageInfo
.
title
=
""
pageInfo
.
title
=
""
pageInfo
.
icon
=
""
pageInfo
.
icon
=
""
const
loadTimeout
=
1000
*
1
const
loadTimeout
=
1000
*
1
00
try
{
try
{
await
new
Promise
<
void
>
((
resolve
,
reject
)
=>
{
await
new
Promise
<
void
>
((
resolve
,
reject
)
=>
{
...
...
src/components/chatdocs/ChatDocsAddon.vue
View file @
31d553e6
...
@@ -10,6 +10,7 @@ import { getDocItem, devConfig, type SiteConfig } from "./helper"
...
@@ -10,6 +10,7 @@ import { getDocItem, devConfig, type SiteConfig } from "./helper"
import
config
from
"@/assets/config.json"
import
config
from
"@/assets/config.json"
import
{
getLocal
}
from
"@/utils/ext"
import
{
getLocal
}
from
"@/utils/ext"
import
{
chatDocPrompt
}
from
"@/utils/prompt"
import
{
chatDocPrompt
}
from
"@/utils/prompt"
import
{
autoPointerCapture
}
from
"@/utils/dom"
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
const
logoUrl
=
chrome
.
runtime
.
getURL
(
"/logo.svg"
)
const
logoUrl
=
chrome
.
runtime
.
getURL
(
"/logo.svg"
)
...
@@ -188,8 +189,7 @@ onUnmounted(() => {
...
@@ -188,8 +189,7 @@ onUnmounted(() => {
>
>
<div
<div
class=
"flex items-center px-4 pt-4 pb-1 select-none"
class=
"flex items-center px-4 pt-4 pb-1 select-none"
@
pointerdown=
"(e) => e.buttons == 1 && (e.target as Element)?.setPointerCapture(e.pointerId)
@
pointerdown=
"autoPointerCapture"
"
@
pointermove=
"handlePointerMove"
@
pointermove=
"handlePointerMove"
@
pointerup=
"adjustPosition"
@
pointerup=
"adjustPosition"
@
pointercancel=
"adjustPosition"
@
pointercancel=
"adjustPosition"
...
...
src/components/icons/IconDockToLeft.vue
0 → 100644
View file @
31d553e6
<
template
>
<svg
xmlns=
"http://www.w3.org/2000/svg"
height=
"24"
viewBox=
"0 -960 960 960"
width=
"24"
fill=
"currentColor"
>
<path
d=
"M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm440-80h120v-560H640v560Zm-80 0v-560H200v560h360Zm80 0h120-120Z"
/>
</svg>
</
template
>
src/components/sidebar/ContentSidebar.vue
0 → 100644
View file @
31d553e6
<
script
setup
lang=
"ts"
>
import
{
debounce
}
from
"lodash-es"
import
{
onMounted
,
onUnmounted
,
ref
,
watch
}
from
"vue"
import
{
getLocal
}
from
"@/utils/ext"
import
IconClose
from
"@/components/icons/IconClose.vue"
import
IconSplitscreenRight
from
"@/components/icons/IconSplitscreenRight.vue"
import
PageScrollbar
from
"./PageScrollbar.vue"
import
{
MessageType
}
from
"@/types"
import
{
autoPointerCapture
}
from
"@/utils/dom"
const
props
=
defineProps
<
{
hidden
?:
boolean
}
>
()
const
emit
=
defineEmits
([
"close"
,
"hide"
])
const
sidebarHtml
=
chrome
.
runtime
.
getURL
(
"sidebar.html"
)
const
sidebarUrl
=
sidebarHtml
+
"?mode=content"
const
logoUrl
=
chrome
.
runtime
.
getURL
(
"/logo.svg"
)
let
sheet
:
CSSStyleSheet
|
null
=
null
let
invisibleTimer
=
0
const
width
=
ref
(
360
)
const
invisible
=
ref
(
false
)
const
pointermove
=
(
e
:
PointerEvent
)
=>
{
if
(
e
.
buttons
)
{
width
.
value
-=
e
.
movementX
}
}
function
updatePageStyle
(
width
=
0
)
{
if
(
!
sheet
)
{
sheet
=
new
CSSStyleSheet
()
document
.
adoptedStyleSheets
=
[...
document
.
adoptedStyleSheets
,
sheet
]
}
const
style
=
`html::-webkit-scrollbar { display: none; } `
+
`html { max-width: calc(100% -
${
width
}
px); }`
sheet
.
replace
(
style
)
sheet
.
disabled
=
false
}
function
disablePageStyle
()
{
if
(
sheet
)
{
sheet
.
disabled
=
true
}
}
function
handleKeydown
(
e
:
KeyboardEvent
)
{
// console.log(e.key, e)
if
(
e
.
key
==
"Escape"
&&
!
invisible
.
value
)
{
invisibleTimer
=
window
.
setTimeout
(()
=>
{
invisible
.
value
=
true
},
300
)
}
}
function
handleKeyUp
(
e
:
KeyboardEvent
)
{
if
(
e
.
key
==
"Escape"
)
{
window
.
clearTimeout
(
invisibleTimer
)
invisible
.
value
=
false
}
}
watch
(
width
,
debounce
((
value
)
=>
{
updatePageStyle
(
value
)
chrome
.
storage
.
local
.
set
({
contentSidebarWidth
:
value
,
})
},
300
)
)
watch
(
()
=>
props
.
hidden
,
()
=>
{
if
(
props
.
hidden
)
{
disablePageStyle
()
}
else
{
updatePageStyle
(
width
.
value
)
}
}
)
onMounted
(()
=>
{
getLocal
({
contentSidebarWidth
:
360
}).
then
((
data
)
=>
{
width
.
value
=
data
.
contentSidebarWidth
})
updatePageStyle
(
width
.
value
)
window
.
addEventListener
(
"keydown"
,
handleKeydown
)
window
.
addEventListener
(
"keyup"
,
handleKeyUp
)
chrome
.
runtime
.
sendMessage
({
type
:
MessageType
.
registerContentSidebar
,
})
})
onUnmounted
(()
=>
{
disablePageStyle
()
window
.
removeEventListener
(
"keydown"
,
handleKeydown
)
window
.
removeEventListener
(
"keyup"
,
handleKeyUp
)
chrome
.
runtime
.
sendMessage
({
type
:
MessageType
.
unregisterContentSidebar
,
})
})
</
script
>
<
template
>
<div
:class=
"[
'fixed flex right-0 top-0 h-screen min-w-80 max-w-[80vw] z-[999999]',
{ invisible: invisible || props.hidden },
]"
:style="{ width: `${width}px` }"
>
<PageScrollbar
class=
"ml-[-2px]"
/>
<div
class=
"relative flex-1 flex flex-col bg-background"
>
<div
class=
"absolute h-full w-1 top-0 left-[-2px] cursor-ew-resize"
@
pointerdown=
"autoPointerCapture"
@
pointermove=
"pointermove"
></div>
<div
class=
"flex gap-2 items-center justify-between h-9 px-2"
>
<button
@
click=
""
class=
"size-6 flex items-center justify-center mr-auto"
>
<img
class=
"size-4"
:src=
"logoUrl"
/>
</button>
<button
@
click=
"emit('hide')"
class=
"size-6 rounded-full hover:bg-background-soft flex items-center justify-center"
>
<IconSplitscreenRight
class=
"size-4 scale-95"
/>
</button>
<button
@
click=
"emit('close')"
class=
"size-6 rounded-full hover:bg-background-soft flex items-center justify-center"
>
<IconClose
class=
"size-4"
/>
</button>
</div>
<iframe
class=
"w-full h-full flex-1"
:src=
"sidebarUrl"
></iframe>
</div>
</div>
</
template
>
<
style
scoped
></
style
>
src/components/sidebar/ContentSidebarAddon.vue
0 → 100644
View file @
31d553e6
<
script
setup
lang=
"ts"
>
import
{
ref
}
from
"vue"
import
ContentSidebar
from
"./ContentSidebar.vue"
import
{
sidebarAddon
}
from
"@/store"
import
{
autoPointerCapture
}
from
"@/utils/dom"
const
logoUrl
=
chrome
.
runtime
.
getURL
(
"/logo.svg"
)
const
top
=
ref
(
72
)
function
handlePointerMove
(
e
:
PointerEvent
)
{
if
(
e
.
buttons
)
{
const
value
=
top
.
value
+
e
.
movementY
top
.
value
=
Math
.
max
(
12
,
Math
.
min
(
window
.
innerHeight
-
60
,
value
))
}
}
</
script
>
<
template
>
<ContentSidebar
v-if=
"sidebarAddon.visible"
:hidden=
"sidebarAddon.hidden"
@
close=
"sidebarAddon.visible = false"
@
hide=
"sidebarAddon.hidden = true"
/>
<div
v-if=
"sidebarAddon.hidden"
:class=
"[
'fixed top-16 right-0 p-1.5 bg-background/65 backdrop-blur-md cursor-pointer',
'rounded-s shadow-lg z-[999999] hover:bg-background-mute/65',
]"
:style=
"
{ top: top + 'px' }"
@pointerdown="autoPointerCapture"
@pointermove="handlePointerMove"
@click="sidebarAddon.hidden = false"
>
<img
:src=
"logoUrl"
class=
"size-4 pointer-events-none select-none"
/>
</div>
</
template
>
<
style
scoped
></
style
>
src/components/sidebar/PageScrollbar.vue
0 → 100644
View file @
31d553e6
<
script
setup
lang=
"ts"
>
import
{
nextTick
,
onMounted
,
onUnmounted
,
ref
}
from
"vue"
const
props
=
defineProps
<
{
class
?:
string
}
>
()
const
scrollEl
=
ref
<
HTMLElement
>
()
const
barWidth
=
ref
(
18
)
const
height
=
ref
(
0
)
const
top
=
ref
(
0
)
const
html
=
document
.
documentElement
let
updateAt
=
0
let
syncAt
=
0
let
operate
=
"sync"
onMounted
(()
=>
{
const
{
scrollHeight
,
scrollTop
}
=
html
height
.
value
=
scrollHeight
top
.
value
=
scrollTop
nextTick
(()
=>
{
if
(
scrollEl
.
value
)
{
barWidth
.
value
+=
1
-
scrollEl
.
value
.
clientWidth
}
})
document
.
addEventListener
(
"scroll"
,
handleScroll
)
})
onUnmounted
(()
=>
{
document
.
removeEventListener
(
"scroll"
,
handleScroll
)
})
function
updateScroll
(
e
:
UIEvent
)
{
updateAt
=
e
.
timeStamp
const
el
=
e
.
target
as
HTMLElement
if
(
updateAt
-
syncAt
>
200
)
{
operate
=
"update"
}
if
(
operate
==
"update"
)
{
// html.scrollTop = el.scrollTop
html
.
scrollTo
({
top
:
el
.
scrollTop
,
behavior
:
"instant"
})
}
}
function
handleScroll
(
e
:
Event
)
{
syncAt
=
e
.
timeStamp
const
{
scrollHeight
,
scrollTop
}
=
html
height
.
value
=
scrollHeight
top
.
value
=
scrollTop
const
bar
=
scrollEl
.
value
if
(
!
bar
)
return
if
(
syncAt
-
updateAt
>
200
)
{
operate
=
"sync"
barWidth
.
value
+=
1
-
bar
.
clientWidth
}
if
(
scrollEl
.
value
&&
operate
==
"sync"
)
{
scrollEl
.
value
.
scrollTop
=
scrollTop
}
}
</
script
>
<
template
>
<div
ref=
"scrollEl"
:class=
"['h-full overflow-y-auto', props.class]"
:style=
"
{ width: barWidth + 'px' }"
@scroll="updateScroll"
>
<div
class=
"w-0"
:style=
"
{ height: height + 'px' }">
</div>
</div>
</
template
>
<
style
scoped
></
style
>
src/content/App.vue
View file @
31d553e6
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
pipLauncher
}
from
"@/store"
import
{
pipLauncher
}
from
"@/store"
import
PipLauncher
from
"@/components/PipLauncher.vue"
import
PipLauncher
from
"@/components/PipLauncher.vue"
import
{
MessageType
}
from
"@/types"
import
{
useI18n
}
from
"vue-i18n"
import
{
useI18n
}
from
"vue-i18n"
import
ChatDocsAddon
from
"@/components/chatdocs/ChatDocsAddon.vue"
import
ChatDocsAddon
from
"@/components/chatdocs/ChatDocsAddon.vue"
import
ContentSidebarAddon
from
"@/components/sidebar/ContentSidebarAddon.vue"
import
{
onMounted
}
from
"vue"
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
const
topFrame
=
window
.
parent
==
window
onMounted
(()
=>
{
chrome
.
runtime
?.
sendMessage
({
type
:
MessageType
.
contentMounted
})
})
</
script
>
</
script
>
<
template
>
<
template
>
...
@@ -15,4 +23,5 @@ const { t } = useI18n()
...
@@ -15,4 +23,5 @@ const { t } = useI18n()
/>
/>
<ChatDocsAddon
/>
<ChatDocsAddon
/>
</
template
>
<ContentSidebarAddon
v-if=
"topFrame"
/>
\ No newline at end of file
</
template
>
src/content/frame.ts
deleted
100644 → 0
View file @
1d61640e
import
{
MessageType
}
from
"@/types"
chrome
.
runtime
.
sendMessage
({
type
:
MessageType
.
frameReady
,
})
src/content/index.ts
View file @
31d553e6
import
{
mount
,
waitMountApp
}
from
"./ui"
import
{
mount
,
waitMountApp
}
from
"./ui"
import
{
chatDocsPanel
,
pipLauncher
,
pipLoading
,
pipWindow
}
from
"@/store"
import
{
chatDocsPanel
,
pipLauncher
,
pipLoading
,
pipWindow
,
sidebarAddon
,
}
from
"@/store"
import
{
ContentEventType
,
FrameMessageType
,
MessageType
}
from
"@/types"
import
{
ContentEventType
,
FrameMessageType
,
MessageType
}
from
"@/types"
import
Copilot
from
"./Copilot.vue"
import
Copilot
from
"./Copilot.vue"
import
{
waitMessage
}
from
"@/utils/ext"
import
{
waitMessage
}
from
"@/utils/ext"
...
@@ -46,6 +52,11 @@ function handleMessage(
...
@@ -46,6 +52,11 @@ function handleMessage(
case
MessageType
.
showChatDocs
:
case
MessageType
.
showChatDocs
:
chatDocsPanel
.
visible
=
true
chatDocsPanel
.
visible
=
true
break
break
case
MessageType
.
openContentSidebar
:
sidebarAddon
.
visible
=
true
sidebarAddon
.
hidden
=
false
sidebarAddon
.
url
=
message
.
url
break
}
}
}
}
...
@@ -115,7 +126,7 @@ function run() {
...
@@ -115,7 +126,7 @@ function run() {
async
function
postPageInfo
()
{
async
function
postPageInfo
()
{
await
new
Promise
((
r
)
=>
setTimeout
(
r
,
1000
*
3
))
await
new
Promise
((
r
)
=>
setTimeout
(
r
,
1000
*
3
))
window
.
top
?.
postMessage
(
window
.
parent
?.
postMessage
(
{
{
type
:
FrameMessageType
.
pageInfo
,
type
:
FrameMessageType
.
pageInfo
,
url
:
location
.
href
,
url
:
location
.
href
,
...
@@ -126,27 +137,30 @@ async function postPageInfo() {
...
@@ -126,27 +137,30 @@ async function postPageInfo() {
)
)
}
}
if
(
window
.
self
==
window
.
top
)
{
function
handleFrameMessage
(
e
:
MessageEvent
)
{
if
(
!
e
.
data
||
typeof
e
.
data
!==
"object"
)
return
const
type
=
e
.
data
.
type
switch
(
type
)
{
case
FrameMessageType
.
contentRun
:
run
()
postPageInfo
()
return
case
FrameMessageType
.
escapeLoad
:
return
dispatchContentEvent
({
type
:
ContentEventType
.
escapeLoad
,
detail
:
{
url
:
e
.
data
.
url
},
})
}
}
if
(
window
.
self
==
window
.
parent
)
{
run
()
run
()
}
else
{
}
else
{
window
.
addEventListener
(
"message"
,
(
e
)
=>
{
window
.
addEventListener
(
"message"
,
handleFrameMessage
)
if
(
!
e
.
data
||
typeof
e
.
data
!==
"object"
)
return
window
.
parent
?.
postMessage
(
const
type
=
e
.
data
.
type
switch
(
type
)
{
case
FrameMessageType
.
contentRun
:
run
()
postPageInfo
()
return
case
FrameMessageType
.
escapeLoad
:
return
dispatchContentEvent
({
type
:
ContentEventType
.
escapeLoad
,
detail
:
{
url
:
e
.
data
.
url
},
})
}
})
window
.
top
?.
postMessage
(
{
{
type
:
FrameMessageType
.
frameReady
,
type
:
FrameMessageType
.
frameReady
,
url
:
location
.
href
,
},
},
chrome
.
runtime
.
getURL
(
""
)
chrome
.
runtime
.
getURL
(
""
)
)
)
...
...
src/content/ui.ts
View file @
31d553e6
...
@@ -33,7 +33,6 @@ export function mount(App: Component, doc = document) {
...
@@ -33,7 +33,6 @@ export function mount(App: Component, doc = document) {
export
function
mountApp
(
doc
=
document
)
{
export
function
mountApp
(
doc
=
document
)
{
mount
(
App
,
doc
)
mount
(
App
,
doc
)
chrome
.
runtime
?.
sendMessage
({
type
:
MessageType
.
contentMount
})
}
}
export
function
waitMountApp
()
{
export
function
waitMountApp
()
{
...
...
src/locales/am.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"እንደሚቆጠር መላክ"
,
"autoSending"
:
"እንደሚቆጠር መላክ"
,
"chooseContentRelevant"
:
"ከመረጡ የሚያሳውቁ ተግባራዎችን ለመረጡ ይችላሉ"
,
"chooseContentRelevant"
:
"ከመረጡ የሚያሳውቁ ተግባራዎችን ለመረጡ ይችላሉ"
,
"notSupported"
:
"ይህ ገጽ ራስ-ሰር መላክን አይደግፍም. እባክዎ መልዕክቱን ይቅዱ እና እራስዎ ይላኩ."
"notSupported"
:
"ይህ ገጽ ራስ-ሰር መላክን አይደግፍም. እባክዎ መልዕክቱን ይቅዱ እና እራስዎ ይላኩ."
}
},
"openSidebar"
:
"የጎን አሞሌውን ይክፈቱ"
}
}
\ No newline at end of file
src/locales/ar.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"الإرسال التلقائي"
,
"autoSending"
:
"الإرسال التلقائي"
,
"chooseContentRelevant"
:
"اختر محتوى أكثر صلة بالموضوع الذي ترغب في التعلم عنه"
,
"chooseContentRelevant"
:
"اختر محتوى أكثر صلة بالموضوع الذي ترغب في التعلم عنه"
,
"notSupported"
:
"هذه الصفحة لا تدعم الإرسال التلقائي. يرجى نسخ الرسالة وإرسالها يدويًا."
"notSupported"
:
"هذه الصفحة لا تدعم الإرسال التلقائي. يرجى نسخ الرسالة وإرسالها يدويًا."
}
},
"openSidebar"
:
"افتح الشريط الجانبي"
}
}
\ No newline at end of file
src/locales/bg.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Автоматично изпращане"
,
"autoSending"
:
"Автоматично изпращане"
,
"chooseContentRelevant"
:
"Изберете съдържание, свързано с темата, за която искате да научите повече"
,
"chooseContentRelevant"
:
"Изберете съдържание, свързано с темата, за която искате да научите повече"
,
"notSupported"
:
"Тази страница не поддържа автоматично изпращане. Моля, копирайте съобщението и го изпратете ръчно."
"notSupported"
:
"Тази страница не поддържа автоматично изпращане. Моля, копирайте съобщението и го изпратете ръчно."
}
},
"openSidebar"
:
"Отворете страничната лента"
}
}
\ No newline at end of file
src/locales/bn.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"অটো প্রেরণ"
,
"autoSending"
:
"অটো প্রেরণ"
,
"chooseContentRelevant"
:
"আপনি যে বিষয়ে আরও জানতে চান তা সম্পর্কিত কনটেন্ট চয়ন করুন"
,
"chooseContentRelevant"
:
"আপনি যে বিষয়ে আরও জানতে চান তা সম্পর্কিত কনটেন্ট চয়ন করুন"
,
"notSupported"
:
"এই পৃষ্ঠাটি স্বয়ংক্রিয় প্রেরণকে সমর্থন করে না। দয়া করে বার্তাটি অনুলিপি করুন এবং এটি ম্যানুয়ালি প্রেরণ করুন।"
"notSupported"
:
"এই পৃষ্ঠাটি স্বয়ংক্রিয় প্রেরণকে সমর্থন করে না। দয়া করে বার্তাটি অনুলিপি করুন এবং এটি ম্যানুয়ালি প্রেরণ করুন।"
}
},
"openSidebar"
:
"সাইডবারটি খুলুন"
}
}
\ No newline at end of file
src/locales/ca.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Enviament automàtic"
,
"autoSending"
:
"Enviament automàtic"
,
"chooseContentRelevant"
:
"Trieu contingut més rellevant pel tema que voleu aprendre"
,
"chooseContentRelevant"
:
"Trieu contingut més rellevant pel tema que voleu aprendre"
,
"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"
}
}
\ No newline at end of file
src/locales/cs.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatické odesílání"
,
"autoSending"
:
"Automatické odesílání"
,
"chooseContentRelevant"
:
"Vyberte obsah více relevantní k tématu, které chcete studovat"
,
"chooseContentRelevant"
:
"Vyberte obsah více relevantní k tématu, které chcete studovat"
,
"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"
}
}
\ No newline at end of file
src/locales/da.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Auto afsendelse"
,
"autoSending"
:
"Auto afsendelse"
,
"chooseContentRelevant"
:
"Vælg indhold mere relevant for det emne, du ønsker at lære om"
,
"chooseContentRelevant"
:
"Vælg indhold mere relevant for det emne, du ønsker at lære om"
,
"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"
}
}
\ No newline at end of file
src/locales/de.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatisches Senden"
,
"autoSending"
:
"Automatisches Senden"
,
"chooseContentRelevant"
:
"Wählen Sie Inhalte, die zum gewünschten Thema passen"
,
"chooseContentRelevant"
:
"Wählen Sie Inhalte, die zum gewünschten Thema passen"
,
"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"
}
}
\ No newline at end of file
src/locales/el.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Αυτόματη αποστολή"
,
"autoSending"
:
"Αυτόματη αποστολή"
,
"chooseContentRelevant"
:
"Επιλέξτε περιεχόμενο που σχετίζεται περισσότερο με το θέμα που θέλετε να μάθετε"
,
"chooseContentRelevant"
:
"Επιλέξτε περιεχόμενο που σχετίζεται περισσότερο με το θέμα που θέλετε να μάθετε"
,
"notSupported"
:
"Αυτή η σελίδα δεν υποστηρίζει αυτόματη αποστολή. Αντιγράψτε το μήνυμα και στείλτε το χειροκίνητα."
"notSupported"
:
"Αυτή η σελίδα δεν υποστηρίζει αυτόματη αποστολή. Αντιγράψτε το μήνυμα και στείλτε το χειροκίνητα."
}
},
"openSidebar"
:
"Ανοίξτε την πλαϊνή μπάρα"
}
}
\ No newline at end of file
src/locales/en.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Auto Sending"
,
"autoSending"
:
"Auto Sending"
,
"chooseContentRelevant"
:
"Choose content more relevant to the topic you want to learn about"
,
"chooseContentRelevant"
:
"Choose content more relevant to the topic you want to learn about"
,
"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"
}
}
\ No newline at end of file
src/locales/es-419.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Envío Automático"
,
"autoSending"
:
"Envío Automático"
,
"chooseContentRelevant"
:
"Elige contenido más relevante para el tema que deseas aprender"
,
"chooseContentRelevant"
:
"Elige contenido más relevante para el tema que deseas aprender"
,
"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"
}
}
\ No newline at end of file
src/locales/es.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Envío Automático"
,
"autoSending"
:
"Envío Automático"
,
"chooseContentRelevant"
:
"Elige contenido más relevante para el tema que deseas aprender"
,
"chooseContentRelevant"
:
"Elige contenido más relevante para el tema que deseas aprender"
,
"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"
}
}
\ No newline at end of file
src/locales/et.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automaatne Saatmine"
,
"autoSending"
:
"Automaatne Saatmine"
,
"chooseContentRelevant"
:
"Valige teema kohta rohkem seotud sisu"
,
"chooseContentRelevant"
:
"Valige teema kohta rohkem seotud sisu"
,
"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"
}
}
\ No newline at end of file
src/locales/fa.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"ارسال خودکار"
,
"autoSending"
:
"ارسال خودکار"
,
"chooseContentRelevant"
:
"محتوای مرتبط با موضوعی که میخواهید درباره آن یاد بگیرید را انتخاب کنید"
,
"chooseContentRelevant"
:
"محتوای مرتبط با موضوعی که میخواهید درباره آن یاد بگیرید را انتخاب کنید"
,
"notSupported"
:
"این صفحه از ارسال خودکار پشتیبانی نمی کند. لطفا پیام را کپی کرده و به صورت دستی ارسال کنید."
"notSupported"
:
"این صفحه از ارسال خودکار پشتیبانی نمی کند. لطفا پیام را کپی کرده و به صورت دستی ارسال کنید."
}
},
"openSidebar"
:
"نوار کناری را باز کنید"
}
}
\ No newline at end of file
src/locales/fi.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automaattilähetys"
,
"autoSending"
:
"Automaattilähetys"
,
"chooseContentRelevant"
:
"Valitse aiheeseesi liittyvämpi sisältö"
,
"chooseContentRelevant"
:
"Valitse aiheeseesi liittyvämpi sisältö"
,
"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"
}
}
\ No newline at end of file
src/locales/fil.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Auto Padala"
,
"autoSending"
:
"Auto Padala"
,
"chooseContentRelevant"
:
"Pumili ng nilalaman na mas kaugnay sa paksa na nais mong malaman"
,
"chooseContentRelevant"
:
"Pumili ng nilalaman na mas kaugnay sa paksa na nais mong malaman"
,
"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"
}
}
\ No newline at end of file
src/locales/fr.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Envoi automatique"
,
"autoSending"
:
"Envoi automatique"
,
"chooseContentRelevant"
:
"Choisissez un contenu plus pertinent pour le sujet que vous souhaitez apprendre"
,
"chooseContentRelevant"
:
"Choisissez un contenu plus pertinent pour le sujet que vous souhaitez apprendre"
,
"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"
}
}
\ No newline at end of file
src/locales/gu.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"આપતી મોકલવું"
,
"autoSending"
:
"આપતી મોકલવું"
,
"chooseContentRelevant"
:
"તમારા શીખવાના વિષય સાથે સંબંધિત કન્ટેન્ટ પસંદ કરો"
,
"chooseContentRelevant"
:
"તમારા શીખવાના વિષય સાથે સંબંધિત કન્ટેન્ટ પસંદ કરો"
,
"notSupported"
:
"આ પૃષ્ઠ સ્વચાલિત મોકલવાનું સમર્થન કરતું નથી. કૃપા કરીને સંદેશની નકલ કરો અને તેને જાતે મોકલો."
"notSupported"
:
"આ પૃષ્ઠ સ્વચાલિત મોકલવાનું સમર્થન કરતું નથી. કૃપા કરીને સંદેશની નકલ કરો અને તેને જાતે મોકલો."
}
},
"openSidebar"
:
"સાઇડબાર ખોલો"
}
}
\ No newline at end of file
src/locales/he.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"שליחה אוטומטית"
,
"autoSending"
:
"שליחה אוטומטית"
,
"chooseContentRelevant"
:
"בחר תוכן הקשור יותר לנושא שברצונך ללמוד עליו"
,
"chooseContentRelevant"
:
"בחר תוכן הקשור יותר לנושא שברצונך ללמוד עליו"
,
"notSupported"
:
"דף זה אינו תומך בשליחה אוטומטית. אנא העתק את ההודעה ושלח אותה ידנית."
"notSupported"
:
"דף זה אינו תומך בשליחה אוטומטית. אנא העתק את ההודעה ושלח אותה ידנית."
}
},
"openSidebar"
:
"פתח את סרגל הצד"
}
}
\ No newline at end of file
src/locales/hi.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"आत्म-भेजन"
,
"autoSending"
:
"आत्म-भेजन"
,
"chooseContentRelevant"
:
"उस विषय के बारे में सीखना जिस पर आप चर्चा करना चाहते हैं, उससे संबंधित सामग्री चुनें"
,
"chooseContentRelevant"
:
"उस विषय के बारे में सीखना जिस पर आप चर्चा करना चाहते हैं, उससे संबंधित सामग्री चुनें"
,
"notSupported"
:
"यह पृष्ठ स्वचालित भेजने का समर्थन नहीं करता है। कृपया संदेश कॉपी करें और इसे मैन्युअल रूप से भेजें।"
"notSupported"
:
"यह पृष्ठ स्वचालित भेजने का समर्थन नहीं करता है। कृपया संदेश कॉपी करें और इसे मैन्युअल रूप से भेजें।"
}
},
"openSidebar"
:
"साइडबार खोलें"
}
}
\ No newline at end of file
src/locales/hr.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatsko slanje"
,
"autoSending"
:
"Automatsko slanje"
,
"chooseContentRelevant"
:
"Odaberite sadržaj koji je relevantan za temu koju želite naučiti"
,
"chooseContentRelevant"
:
"Odaberite sadržaj koji je relevantan za temu koju želite naučiti"
,
"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"
}
}
\ No newline at end of file
src/locales/hu.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatikus küldés"
,
"autoSending"
:
"Automatikus küldés"
,
"chooseContentRelevant"
:
"Válassza ki a témához relevánsabb tartalmat, amiről szeretne tanulni"
,
"chooseContentRelevant"
:
"Válassza ki a témához relevánsabb tartalmat, amiről szeretne tanulni"
,
"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"
}
}
\ No newline at end of file
src/locales/id.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Pengiriman Otomatis"
,
"autoSending"
:
"Pengiriman Otomatis"
,
"chooseContentRelevant"
:
"Pilih konten yang lebih relevan dengan topik yang ingin Anda pelajari"
,
"chooseContentRelevant"
:
"Pilih konten yang lebih relevan dengan topik yang ingin Anda pelajari"
,
"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"
}
}
\ No newline at end of file
src/locales/it.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Invio automatico"
,
"autoSending"
:
"Invio automatico"
,
"chooseContentRelevant"
:
"Scegli contenuti più pertinenti all'argomento che vuoi apprendere"
,
"chooseContentRelevant"
:
"Scegli contenuti più pertinenti all'argomento che vuoi apprendere"
,
"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"
}
}
\ No newline at end of file
src/locales/ja.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"自動送信"
,
"autoSending"
:
"自動送信"
,
"chooseContentRelevant"
:
"学びたいトピックに関連するコンテンツを選択してください"
,
"chooseContentRelevant"
:
"学びたいトピックに関連するコンテンツを選択してください"
,
"notSupported"
:
"このページは、自動送信をサポートしていません。メッセージをコピーして手動で送信してください。"
"notSupported"
:
"このページは、自動送信をサポートしていません。メッセージをコピーして手動で送信してください。"
}
},
"openSidebar"
:
"サイドバーを開きます"
}
}
\ No newline at end of file
src/locales/kn.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"ಸ್ವಯಂ ಕಳುಹಿಸುತ್ತಿದೆ"
,
"autoSending"
:
"ಸ್ವಯಂ ಕಳುಹಿಸುತ್ತಿದೆ"
,
"chooseContentRelevant"
:
"ನೀವು ಕಲಿಯಬಯಸುವ ವಿಷಯಕ್ಕೆ ಹೆಚ್ಚಿನ ಸಂಬಂಧಪಟ್ಟ ವಿಷಯಗಳನ್ನು ಆರಿಸಿ"
,
"chooseContentRelevant"
:
"ನೀವು ಕಲಿಯಬಯಸುವ ವಿಷಯಕ್ಕೆ ಹೆಚ್ಚಿನ ಸಂಬಂಧಪಟ್ಟ ವಿಷಯಗಳನ್ನು ಆರಿಸಿ"
,
"notSupported"
:
"ಈ ಪುಟವು ಸ್ವಯಂಚಾಲಿತ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ. ದಯವಿಟ್ಟು ಸಂದೇಶವನ್ನು ನಕಲಿಸಿ ಮತ್ತು ಅದನ್ನು ಕೈಯಾರೆ ಕಳುಹಿಸಿ."
"notSupported"
:
"ಈ ಪುಟವು ಸ್ವಯಂಚಾಲಿತ ಕಳುಹಿಸುವಿಕೆಯನ್ನು ಬೆಂಬಲಿಸುವುದಿಲ್ಲ. ದಯವಿಟ್ಟು ಸಂದೇಶವನ್ನು ನಕಲಿಸಿ ಮತ್ತು ಅದನ್ನು ಕೈಯಾರೆ ಕಳುಹಿಸಿ."
}
},
"openSidebar"
:
"ಸೈಡ್ಬಾರ್ ತೆರೆಯಿರಿ"
}
}
\ No newline at end of file
src/locales/ko.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"자동 전송"
,
"autoSending"
:
"자동 전송"
,
"chooseContentRelevant"
:
"학습하고 싶은 주제와 관련된 콘텐츠를 선택하세요"
,
"chooseContentRelevant"
:
"학습하고 싶은 주제와 관련된 콘텐츠를 선택하세요"
,
"notSupported"
:
"이 페이지는 자동 전송을 지원하지 않습니다. 메시지를 복사하여 수동으로 보내주십시오."
"notSupported"
:
"이 페이지는 자동 전송을 지원하지 않습니다. 메시지를 복사하여 수동으로 보내주십시오."
}
},
"openSidebar"
:
"사이드 바를 엽니 다"
}
}
\ No newline at end of file
src/locales/lt.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatinis siuntimas"
,
"autoSending"
:
"Automatinis siuntimas"
,
"chooseContentRelevant"
:
"Pasirinkite turinį, kuris yra labiau susijęs su jumis dominančia tema"
,
"chooseContentRelevant"
:
"Pasirinkite turinį, kuris yra labiau susijęs su jumis dominančia tema"
,
"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ą"
}
}
\ No newline at end of file
src/locales/lv.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automātiska sūtīšana"
,
"autoSending"
:
"Automātiska sūtīšana"
,
"chooseContentRelevant"
:
"Izvēlieties saturu, kas ir saistīts ar tēmu, par kuru vēlaties uzzināt"
,
"chooseContentRelevant"
:
"Izvēlieties saturu, kas ir saistīts ar tēmu, par kuru vēlaties uzzināt"
,
"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"
}
}
\ No newline at end of file
src/locales/ml.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"ഓട്ടോ അയയ്ക്കൽ"
,
"autoSending"
:
"ഓട്ടോ അയയ്ക്കൽ"
,
"chooseContentRelevant"
:
"നിങ്ങളുടെ അറിവിനായി കരുതോട്ട വിഷയത്തിനു കൂടുതൽ ബന്ധമായ ഉള്ളടക്കം തിരഞ്ഞെടുക്കുക"
,
"chooseContentRelevant"
:
"നിങ്ങളുടെ അറിവിനായി കരുതോട്ട വിഷയത്തിനു കൂടുതൽ ബന്ധമായ ഉള്ളടക്കം തിരഞ്ഞെടുക്കുക"
,
"notSupported"
:
"ഈ പേജ് യാന്ത്രിക അയയ്ക്കുന്നതിനെ പിന്തുണയ്ക്കുന്നില്ല. സന്ദേശം പകർത്തി സ്വമേധയാ അയയ്ക്കുക."
"notSupported"
:
"ഈ പേജ് യാന്ത്രിക അയയ്ക്കുന്നതിനെ പിന്തുണയ്ക്കുന്നില്ല. സന്ദേശം പകർത്തി സ്വമേധയാ അയയ്ക്കുക."
}
},
"openSidebar"
:
"സൈഡ്ബാർ തുറക്കുക"
}
}
\ No newline at end of file
src/locales/mr.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"स्वत: पाठवणे"
,
"autoSending"
:
"स्वत: पाठवणे"
,
"chooseContentRelevant"
:
"तुम्हाला ओळखायचं विषयसंबंधित आशय निवडा"
,
"chooseContentRelevant"
:
"तुम्हाला ओळखायचं विषयसंबंधित आशय निवडा"
,
"notSupported"
:
"हे पृष्ठ स्वयंचलित पाठविण्यास समर्थन देत नाही. कृपया संदेश कॉपी करा आणि तो व्यक्तिचलितपणे पाठवा."
"notSupported"
:
"हे पृष्ठ स्वयंचलित पाठविण्यास समर्थन देत नाही. कृपया संदेश कॉपी करा आणि तो व्यक्तिचलितपणे पाठवा."
}
},
"openSidebar"
:
"साइडबार उघडा"
}
}
\ No newline at end of file
src/locales/ms.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Penghantaran Automatik"
,
"autoSending"
:
"Penghantaran Automatik"
,
"chooseContentRelevant"
:
"Pilih kandungan yang lebih berkaitan dengan topik yang anda ingin ketahui"
,
"chooseContentRelevant"
:
"Pilih kandungan yang lebih berkaitan dengan topik yang anda ingin ketahui"
,
"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"
}
}
\ No newline at end of file
src/locales/nl.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatisch verzenden"
,
"autoSending"
:
"Automatisch verzenden"
,
"chooseContentRelevant"
:
"Kies inhoud die relevanter is voor het onderwerp dat je wilt leren"
,
"chooseContentRelevant"
:
"Kies inhoud die relevanter is voor het onderwerp dat je wilt leren"
,
"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"
}
}
\ No newline at end of file
src/locales/no.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Auto Sending"
,
"autoSending"
:
"Auto Sending"
,
"chooseContentRelevant"
:
"Velg innhold som er mer relevant for emnet du vil lære om"
,
"chooseContentRelevant"
:
"Velg innhold som er mer relevant for emnet du vil lære om"
,
"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"
}
}
\ No newline at end of file
src/locales/pl.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatyczne Wysyłanie"
,
"autoSending"
:
"Automatyczne Wysyłanie"
,
"chooseContentRelevant"
:
"Wybierz treść bardziej związana z tematem, który chcesz się dowiedzieć"
,
"chooseContentRelevant"
:
"Wybierz treść bardziej związana z tematem, który chcesz się dowiedzieć"
,
"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"
}
}
\ No newline at end of file
src/locales/pt-BR.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Envio Automático"
,
"autoSending"
:
"Envio Automático"
,
"chooseContentRelevant"
:
"Escolha conteúdo mais relevante para o tópico que você deseja aprender"
,
"chooseContentRelevant"
:
"Escolha conteúdo mais relevante para o tópico que você deseja aprender"
,
"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"
}
}
\ No newline at end of file
src/locales/pt-PT.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Envio Automático"
,
"autoSending"
:
"Envio Automático"
,
"chooseContentRelevant"
:
"Escolha conteúdo mais relevante para o tópico que deseja aprender"
,
"chooseContentRelevant"
:
"Escolha conteúdo mais relevante para o tópico que deseja aprender"
,
"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"
}
}
\ No newline at end of file
src/locales/ro.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Trimitere Automată"
,
"autoSending"
:
"Trimitere Automată"
,
"chooseContentRelevant"
:
"Alegeți conținut mai relevant pentru subiectul pe care doriți să îl învățați"
,
"chooseContentRelevant"
:
"Alegeți conținut mai relevant pentru subiectul pe care doriți să îl învățați"
,
"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ă"
}
}
\ No newline at end of file
src/locales/ru.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Автоматическая Отправка"
,
"autoSending"
:
"Автоматическая Отправка"
,
"chooseContentRelevant"
:
"Выберите более релевантный контент по теме, которую вы хотите изучить"
,
"chooseContentRelevant"
:
"Выберите более релевантный контент по теме, которую вы хотите изучить"
,
"notSupported"
:
"Эта страница не поддерживает автоматическую отправку. Пожалуйста, скопируйте сообщение и отправьте его вручную."
"notSupported"
:
"Эта страница не поддерживает автоматическую отправку. Пожалуйста, скопируйте сообщение и отправьте его вручную."
}
},
"openSidebar"
:
"Откройте боковую панель"
}
}
\ No newline at end of file
src/locales/sk.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatické Odosielanie"
,
"autoSending"
:
"Automatické Odosielanie"
,
"chooseContentRelevant"
:
"Vyberte obsah, ktorý je viac relevantný pre tému, ktorú chcete študovať"
,
"chooseContentRelevant"
:
"Vyberte obsah, ktorý je viac relevantný pre tému, ktorú chcete študovať"
,
"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"
}
}
\ No newline at end of file
src/locales/sl.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Avtomatsko Pošiljanje"
,
"autoSending"
:
"Avtomatsko Pošiljanje"
,
"chooseContentRelevant"
:
"Izberite vsebino, ki je bolj relevantna za temo, ki se je želite naučiti"
,
"chooseContentRelevant"
:
"Izberite vsebino, ki je bolj relevantna za temo, ki se je želite naučiti"
,
"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"
}
}
\ No newline at end of file
src/locales/sr.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatsko Slanje"
,
"autoSending"
:
"Automatsko Slanje"
,
"chooseContentRelevant"
:
"Izaberite sadržaj koji je relevantniji za temu koju želite naučiti"
,
"chooseContentRelevant"
:
"Izaberite sadržaj koji je relevantniji za temu koju želite naučiti"
,
"notSupported"
:
"Ова страница не подржава аутоматско слање. Копирајте поруку и пошаљите га ручно."
"notSupported"
:
"Ова страница не подржава аутоматско слање. Копирајте поруку и пошаљите га ручно."
}
},
"openSidebar"
:
"Отвори бочну траку"
}
}
\ No newline at end of file
src/locales/sv.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Automatisk sändning"
,
"autoSending"
:
"Automatisk sändning"
,
"chooseContentRelevant"
:
"Välj innehåll som är mer relevant för det ämne du vill lära dig om"
,
"chooseContentRelevant"
:
"Välj innehåll som är mer relevant för det ämne du vill lära dig om"
,
"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"
}
}
\ No newline at end of file
src/locales/sw.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Kutuma Kiotomatiki"
,
"autoSending"
:
"Kutuma Kiotomatiki"
,
"chooseContentRelevant"
:
"Chagua yaliyomo inayohusiana zaidi na mada unayotaka kujifunza kuhusu"
,
"chooseContentRelevant"
:
"Chagua yaliyomo inayohusiana zaidi na mada unayotaka kujifunza kuhusu"
,
"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"
}
}
\ No newline at end of file
src/locales/ta.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"தானாக அனுப்புதல்"
,
"autoSending"
:
"தானாக அனுப்புதல்"
,
"chooseContentRelevant"
:
"நீங்கள் அறிந்திருக்க விரும்பும் பகுதிக்கு உரையாடல் தேர்ந்தெடுக்கவும்"
,
"chooseContentRelevant"
:
"நீங்கள் அறிந்திருக்க விரும்பும் பகுதிக்கு உரையாடல் தேர்ந்தெடுக்கவும்"
,
"notSupported"
:
"இந்த பக்கம் தானியங்கி அனுப்புதலை ஆதரிக்காது. தயவுசெய்து செய்தியை நகலெடுத்து கைமுறையாக அனுப்புங்கள்."
"notSupported"
:
"இந்த பக்கம் தானியங்கி அனுப்புதலை ஆதரிக்காது. தயவுசெய்து செய்தியை நகலெடுத்து கைமுறையாக அனுப்புங்கள்."
}
},
"openSidebar"
:
"பக்கப்பட்டியைத் திறக்கவும்"
}
}
\ No newline at end of file
src/locales/te.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"స్వీయం పంపిణీ"
,
"autoSending"
:
"స్వీయం పంపిణీ"
,
"chooseContentRelevant"
:
"మీరు కలిగిన విషయానికి అనుసంధానం కలిగిన కంటెంట్ ఎంచుకోండి"
,
"chooseContentRelevant"
:
"మీరు కలిగిన విషయానికి అనుసంధానం కలిగిన కంటెంట్ ఎంచుకోండి"
,
"notSupported"
:
"ఈ పేజీ ఆటోమేటిక్ పంపడానికి మద్దతు ఇవ్వదు. దయచేసి సందేశాన్ని కాపీ చేసి మానవీయంగా పంపండి."
"notSupported"
:
"ఈ పేజీ ఆటోమేటిక్ పంపడానికి మద్దతు ఇవ్వదు. దయచేసి సందేశాన్ని కాపీ చేసి మానవీయంగా పంపండి."
}
},
"openSidebar"
:
"సైడ్బార్ తెరవండి"
}
}
\ No newline at end of file
src/locales/th.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"การส่งอัตโนมัติ"
,
"autoSending"
:
"การส่งอัตโนมัติ"
,
"chooseContentRelevant"
:
"เลือกเนื้อหาที่เกี่ยวข้องมากขึ้นกับหัวข้อที่คุณต้องการเรียนรู้"
,
"chooseContentRelevant"
:
"เลือกเนื้อหาที่เกี่ยวข้องมากขึ้นกับหัวข้อที่คุณต้องการเรียนรู้"
,
"notSupported"
:
"หน้านี้ไม่รองรับการส่งอัตโนมัติ กรุณาคัดลอกข้อความและส่งด้วยตนเอง"
"notSupported"
:
"หน้านี้ไม่รองรับการส่งอัตโนมัติ กรุณาคัดลอกข้อความและส่งด้วยตนเอง"
}
},
"openSidebar"
:
"เปิดแถบด้านข้าง"
}
}
\ No newline at end of file
src/locales/tr.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Otomatik Gönderim"
,
"autoSending"
:
"Otomatik Gönderim"
,
"chooseContentRelevant"
:
"Öğrenmek istediğiniz konuyla daha ilgili içerik seçin"
,
"chooseContentRelevant"
:
"Öğrenmek istediğiniz konuyla daha ilgili içerik seçin"
,
"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ç"
}
}
\ No newline at end of file
src/locales/uk.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Автоматичне відправлення"
,
"autoSending"
:
"Автоматичне відправлення"
,
"chooseContentRelevant"
:
"Виберіть вміст, який більше відповідає темі, яку ви хочете вивчити"
,
"chooseContentRelevant"
:
"Виберіть вміст, який більше відповідає темі, яку ви хочете вивчити"
,
"notSupported"
:
"Ця сторінка не підтримує автоматичне надсилання. Будь ласка, скопіюйте повідомлення та надішліть його вручну."
"notSupported"
:
"Ця сторінка не підтримує автоматичне надсилання. Будь ласка, скопіюйте повідомлення та надішліть його вручну."
}
},
"openSidebar"
:
"Відкрийте бічну панель"
}
}
\ No newline at end of file
src/locales/vi.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"Tự động Gửi"
,
"autoSending"
:
"Tự động Gửi"
,
"chooseContentRelevant"
:
"Chọn nội dung liên quan hơn đến chủ đề bạn muốn tìm hiểu"
,
"chooseContentRelevant"
:
"Chọn nội dung liên quan hơn đến chủ đề bạn muốn tìm hiểu"
,
"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"
}
}
\ No newline at end of file
src/locales/zh-CN.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"自动发送"
,
"autoSending"
:
"自动发送"
,
"chooseContentRelevant"
:
"选择与你想了解的主题更相关的内容"
,
"chooseContentRelevant"
:
"选择与你想了解的主题更相关的内容"
,
"notSupported"
:
"此页面不支持自动发送,请复制消息发送"
"notSupported"
:
"此页面不支持自动发送,请复制消息发送"
}
},
"openSidebar"
:
"打开侧边栏"
}
}
\ No newline at end of file
src/locales/zh-TW.json
View file @
31d553e6
...
@@ -42,5 +42,6 @@
...
@@ -42,5 +42,6 @@
"autoSending"
:
"自動發送"
,
"autoSending"
:
"自動發送"
,
"chooseContentRelevant"
:
"選擇與你想了解的主題更相關的內容"
,
"chooseContentRelevant"
:
"選擇與你想了解的主題更相關的內容"
,
"notSupported"
:
"此頁面不支持自動發送。請複制消息並手動發送。"
"notSupported"
:
"此頁面不支持自動發送。請複制消息並手動發送。"
}
},
"openSidebar"
:
"打開側邊欄"
}
}
\ No newline at end of file
src/manifest.ts
View file @
31d553e6
...
@@ -77,7 +77,6 @@ const manifest = {
...
@@ -77,7 +77,6 @@ const manifest = {
"sidePanel"
,
"sidePanel"
,
"declarativeNetRequestWithHostAccess"
,
"declarativeNetRequestWithHostAccess"
,
"declarativeNetRequestFeedback"
,
"declarativeNetRequestFeedback"
,
"webNavigation"
,
],
],
optional_permissions
:
[],
optional_permissions
:
[],
host_permissions
:
[
"<all_urls>"
],
host_permissions
:
[
"<all_urls>"
],
...
@@ -93,11 +92,7 @@ const manifest = {
...
@@ -93,11 +92,7 @@ const manifest = {
},
},
web_accessible_resources
:
[
web_accessible_resources
:
[
{
{
resources
:
[
"logo.svg"
],
resources
:
[
"/js/*"
,
"/assets/*"
,
"sidebar.html"
,
"logo.svg"
],
matches
:
[
"<all_urls>"
],
},
{
resources
:
[
"/js/*"
,
"/assets/*"
],
matches
:
[
"<all_urls>"
],
matches
:
[
"<all_urls>"
],
use_dynamic_url
:
true
,
use_dynamic_url
:
true
,
},
},
...
@@ -107,6 +102,6 @@ const manifest = {
...
@@ -107,6 +102,6 @@ const manifest = {
?
`script-src 'self' http://localhost:3000 'wasm-unsafe-eval';`
?
`script-src 'self' http://localhost:3000 'wasm-unsafe-eval';`
:
`script-src 'self' 'wasm-unsafe-eval'`
,
:
`script-src 'self' 'wasm-unsafe-eval'`
,
},
},
}
satisfies
Manifest
as
chrome
.
runtime
.
Manifest
}
satisfies
Manifest
as
unknown
as
chrome
.
runtime
.
Manifest
export
default
manifest
export
default
manifest
src/pages/Dev.vue
0 → 100644
View file @
31d553e6
<
script
setup
lang=
"ts"
>
import
ContentSidebar
from
"@/components/sidebar/ContentSidebar.vue"
</
script
>
<
template
>
<ContentSidebar
/>
</
template
>
<
style
scoped
></
style
>
src/pages/Popup.vue
View file @
31d553e6
...
@@ -19,17 +19,18 @@ import IconAmpStories from "@/components/icons/IconAmpStories.vue"
...
@@ -19,17 +19,18 @@ import IconAmpStories from "@/components/icons/IconAmpStories.vue"
import
IconGppMaybe
from
"@/components/icons/IconGppMaybe.vue"
import
IconGppMaybe
from
"@/components/icons/IconGppMaybe.vue"
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
IconSplitscreenRight
from
"@/components/icons/IconSplitscreenRight.vue"
import
IconSplitscreenRight
from
"@/components/icons/IconSplitscreenRight.vue"
import
SiteButton
from
"@/components/SiteButton.vue"
const
isEdge
=
/Edg/
.
test
(
navigator
.
userAgent
)
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
const
activeTab
=
ref
<
chrome
.
tabs
.
Tab
>
(
emptyTab
)
const
activeTab
=
ref
<
chrome
.
tabs
.
Tab
>
(
emptyTab
)
const
manifest
=
reactive
(
chrome
.
runtime
.
getManifest
())
const
manifest
=
reactive
(
chrome
.
runtime
.
getManifest
())
const
avaiable
=
ref
(
false
)
const
avaiable
=
ref
(
false
)
const
popularItems
=
reactive
(
config
.
data
.
popularSites
)
const
popularItems
=
reactive
(
config
.
data
.
popularSites
)
const
keyboard
=
reactive
({
const
keyboard
=
reactive
({
ctrl
:
false
,
ctrl
:
false
,
shift
:
false
,
})
})
const
horizontalScroller
=
ref
<
HTMLElement
|
null
>
(
null
)
const
horizontalScroller
=
ref
<
HTMLElement
|
null
>
(
null
)
...
@@ -98,18 +99,24 @@ function handleKeydown(e: KeyboardEvent) {
...
@@ -98,18 +99,24 @@ function handleKeydown(e: KeyboardEvent) {
}
}
if
(
e
.
code
==
"Backslash"
&&
(
e
.
ctrlKey
||
e
.
metaKey
))
{
if
(
e
.
code
==
"Backslash"
&&
(
e
.
ctrlKey
||
e
.
metaKey
))
{
e
.
preventDefault
()
e
.
preventDefault
()
handleO
penSidebar
()
o
penSidebar
()
}
}
if
(
e
.
code
==
"ControlLeft"
||
e
.
code
==
"ControlRight"
)
{
if
(
e
.
code
==
"ControlLeft"
||
e
.
code
==
"ControlRight"
)
{
keyboard
.
ctrl
=
true
keyboard
.
ctrl
=
true
}
}
if
(
e
.
code
==
"ShiftLeft"
||
e
.
code
==
"ShiftRight"
)
{
keyboard
.
shift
=
true
}
}
}
function
hanldeKeyup
(
e
:
KeyboardEvent
)
{
function
hanldeKeyup
(
e
:
KeyboardEvent
)
{
if
(
e
.
code
==
"ControlLeft"
||
e
.
code
==
"ControlRight"
)
{
if
(
e
.
code
==
"ControlLeft"
||
e
.
code
==
"ControlRight"
)
{
keyboard
.
ctrl
=
false
keyboard
.
ctrl
=
false
}
}
if
(
e
.
code
==
"ShiftLeft"
||
e
.
code
==
"ShiftRight"
)
{
keyboard
.
shift
=
false
}
}
}
const
handleLocalChange
=
(
changes
:
{
const
handleLocalChange
=
(
changes
:
{
...
@@ -125,7 +132,7 @@ async function handleWriteHtml() {
...
@@ -125,7 +132,7 @@ async function handleWriteHtml() {
const
tab
=
tabs
[
0
]
const
tab
=
tabs
[
0
]
if
(
tab
)
{
if
(
tab
)
{
chrome
.
tabs
.
sendMessage
(
tab
.
id
!
,
{
chrome
.
tabs
.
sendMessage
(
tab
.
id
!
,
{
type
:
"pip"
,
type
:
MessageType
.
pip
,
options
:
{
options
:
{
url
:
tab
.
url
,
url
:
tab
.
url
,
mode
:
"write-html"
,
mode
:
"write-html"
,
...
@@ -134,18 +141,31 @@ async function handleWriteHtml() {
...
@@ -134,18 +141,31 @@ async function handleWriteHtml() {
}
}
}
}
async
function
handleOpenSidebar
(
url
:
string
=
""
)
{
async
function
openSidebar
(
url
=
""
)
{
const
path
=
defaultSidebarPath
+
"?url="
+
encodeURIComponent
(
url
)
await
chrome
.
sidePanel
.
setOptions
({
path
})
const
win
=
await
chrome
.
windows
.
getCurrent
()
const
win
=
await
chrome
.
windows
.
getCurrent
()
await
chrome
.
storage
.
session
.
set
({
sidebarUrls
:
{
sidepanel
:
url
},
})
await
chrome
.
sidePanel
.
open
({
windowId
:
win
.
id
!
})
await
chrome
.
sidePanel
.
open
({
windowId
:
win
.
id
!
})
window
.
close
()
}
async
function
openContentSidebar
(
url
=
""
)
{
await
chrome
.
storage
.
session
.
set
({
sidebarUrls
:
{
content
:
url
},
})
await
chrome
.
tabs
.
sendMessage
(
activeTab
.
value
.
id
!
,
{
type
:
MessageType
.
openContentSidebar
,
url
,
})
window
.
close
()
window
.
close
()
}
}
async
function
handleClickLaunch
(
url
:
string
)
{
async
function
handleClickLaunch
(
url
:
string
)
{
console
.
log
(
activeTab
.
value
)
console
.
log
(
activeTab
.
value
)
await
chrome
.
runtime
.
sendMessage
({
await
chrome
.
runtime
.
sendMessage
({
type
:
"bg-pip-launch"
,
type
:
MessageType
.
bgPipLaunch
,
url
,
url
,
})
})
window
.
close
()
window
.
close
()
...
@@ -207,7 +227,7 @@ function showChatDocs() {
...
@@ -207,7 +227,7 @@ function showChatDocs() {
<div
<div
:class=
"[
:class=
"[
'flex gap-
1
items-center text-sm my-2 px-2',
'flex gap-
2
items-center text-sm my-2 px-2',
{
{
'text-rose-800': !avaiable,
'text-rose-800': !avaiable,
},
},
...
@@ -215,15 +235,15 @@ function showChatDocs() {
...
@@ -215,15 +235,15 @@ function showChatDocs() {
>
>
<div
<div
v-if=
"avaiable"
v-if=
"avaiable"
class=
"
w-4 h-4 rounded
"
class=
"
size-5
"
:style=
"
{
:style=
"
{
background:
background: 'center / contain url(' + activeTab?.favIconUrl + ')',
'#8882 center / contain url(' + activeTab?.favIconUrl + ')',
}"
}"
>
</div>
>
</div>
<IconGppMaybe
v-else
class=
"w-4 h-4"
/>
<IconGppMaybe
v-else
class=
"size-5"
/>
<div
v-if=
"avaiable"
class=
"truncate"
>
{{
host
}}
</div>
<div
class=
"truncate"
>
<div
v-else
class=
"truncate"
>
{{
t
(
"protectedTabTips"
)
}}
</div>
{{
avaiable
?
host
:
t
(
"protectedTabTips"
)
}}
</div>
</div>
</div>
<div
<div
...
@@ -234,10 +254,10 @@ function showChatDocs() {
...
@@ -234,10 +254,10 @@ function showChatDocs() {
>
>
<button
<button
v-if=
"!pipWindow.tab"
v-if=
"!pipWindow.tab"
:disabled=
"!avaiable"
class=
"hover:bg-background-mute bg-background-soft disabled:bg-background-soft disabled:opacity-65"
class=
"hover:bg-background-mute bg-background-soft disabled:bg-background-soft disabled:opacity-65"
@
click=
"handleWriteHtml
"
:disabled=
"!avaiable
"
:title=
"t('openInPip')"
:title=
"t('openInPip')"
@
click=
"handleWriteHtml"
>
>
<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"
>
...
@@ -251,14 +271,22 @@ function showChatDocs() {
...
@@ -251,14 +271,22 @@ function showChatDocs() {
<PipWindowActions
v-else
/>
<PipWindowActions
v-else
/>
<button
<button
class=
"hover:bg-background-mute bg-background-soft"
class=
"hover:bg-background-mute bg-background-soft
disabled:bg-background-soft disabled:opacity-65
"
@
click=
"() => handleOpenSidebar(activeTab.url)
"
:disabled=
"isEdge && !avaiable
"
:title=
"t('openInSidebar')"
:title=
"t('openInSidebar')"
@
click=
"
() =>
{
if (isEdge || keyboard.ctrl) {
return openContentSidebar()
}
openSidebar(activeTab.url)
}
"
>
>
<IconSplitscreenRight
class=
"size-8 shrink-0 scale-95"
/>
<IconSplitscreenRight
class=
"size-8 shrink-0 scale-95"
/>
<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"
>
<span
class=
"text-sm font-bold leading-4 truncate"
>
<span
class=
"text-sm font-bold leading-4 truncate"
>
{{
t
(
"openInSidebar"
)
}}
{{
isEdge
?
t
(
"openSidebar"
)
:
t
(
"openInSidebar"
)
}}
</span>
</span>
<span
class=
"text-xs"
>
CTRL + \
</span>
<span
class=
"text-xs"
>
CTRL + \
</span>
</div>
</div>
...
@@ -310,37 +338,25 @@ function showChatDocs() {
...
@@ -310,37 +338,25 @@ function showChatDocs() {
@
wheel=
"(e) => horizontalScroller!.scrollLeft += e.deltaY"
@
wheel=
"(e) => horizontalScroller!.scrollLeft += e.deltaY"
@
pointermove=
"(e) => e.buttons && (horizontalScroller!.scrollLeft -= e.movementX)"
@
pointermove=
"(e) => e.buttons && (horizontalScroller!.scrollLeft -= e.movementX)"
>
>
<
b
utton
<
SiteB
utton
v-for=
"item of popularItems"
v-for=
"item of popularItems"
class=
"group w-[58px] shrink-0 relative flex flex-col items-center rounded-lg px-2 py-3 bg-background-soft"
:icon=
"item.icon"
:title=
"item.title"
:badge=
"keyboard.shift || !avaiable ? 'popup' : 'sidebar'"
small
@
click=
"
@
click=
"
() =>
() =>
{
keyboard.ctrl
if (keyboard.shift || !avaiable) {
? handleOpenSidebar(item.url)
return handleClickLaunch(item.url)
: handleClickLaunch(item.url)
}
if (isEdge || keyboard.ctrl) {
return openContentSidebar(item.url)
}
openSidebar(item.url)
}
"
"
>
/>
<div
class=
"size-6 rounded"
:style=
"
{
background: 'center / contain url(' + item.icon + ')',
}"
>
</div>
<div
class=
"text-xs h-6 leading-3 line-clamp-2 flex flex-col justify-center"
>
<div>
{{
item
.
title
}}
</div>
</div>
<IconAmpStories
v-if=
"!keyboard.ctrl"
class=
"size-3 absolute top-1 right-1 opacity-0 group-hover:opacity-65"
/>
<IconSplitscreenRight
v-else
class=
"size-3 absolute top-1 right-1 opacity-0 group-hover:opacity-65"
/>
</button>
</div>
</div>
<div
class=
"mt-3 flex items-center justify-center opacity-60"
>
<div
class=
"mt-3 flex items-center justify-center opacity-60"
>
...
...
src/pages/Sidebar.vue
View file @
31d553e6
<
script
setup
lang=
"ts"
>
<
script
setup
lang=
"ts"
>
import
{
ref
,
onMounted
,
reactive
,
computed
}
from
"vue"
import
{
ref
,
onMounted
,
reactive
,
computed
,
onUnmounted
}
from
"vue"
import
{
getLocal
,
updateFrameNetRules
}
from
"@/utils/ext"
import
{
emptyTab
,
getLocal
,
getSession
,
isProtectedUrl
,
updateFrameNetRules
,
}
from
"@/utils/ext"
import
config
from
"@/assets/config.json"
import
config
from
"@/assets/config.json"
import
LoadingBar
from
"@/components/LoadingBar.vue"
import
LoadingBar
from
"@/components/LoadingBar.vue"
import
Webview
from
"@/components/Webview.vue"
import
Webview
from
"@/components/Webview.vue"
import
{
useI18n
}
from
"@/utils/i18n"
import
{
useI18n
}
from
"@/utils/i18n"
import
{
MessageType
}
from
"@/types"
import
SiteButton
from
"@/components/SiteButton.vue"
const
logoUrl
=
chrome
.
runtime
.
getURL
(
"/logo.svg"
)
const
logoUrl
=
chrome
.
runtime
.
getURL
(
"/logo.svg"
)
const
{
t
}
=
useI18n
()
const
{
t
}
=
useI18n
()
const
url
=
ref
(
""
)
const
url
=
ref
(
""
)
const
mode
=
ref
(
""
)
const
popularItems
=
reactive
(
config
.
data
.
popularSites
)
const
popularItems
=
reactive
(
config
.
data
.
popularSites
)
const
recentItems
=
reactive
<
{
url
:
string
;
title
:
string
;
icon
:
string
}[]
>
([])
const
recentItems
=
reactive
<
{
url
:
string
;
title
:
string
;
icon
:
string
}[]
>
([])
const
protectedUrl
=
computed
(()
=>
{
const
protectedUrl
=
computed
(()
=>
{
if
(
!
url
.
value
)
{
return
isProtectedUrl
(
url
.
value
)
return
true
})
const
currentTab
=
reactive
({
tabId
:
0
,
})
async
function
handleMessage
(
message
:
any
)
{
switch
(
message
.
type
)
{
case
MessageType
.
openInSidebar
:
if
(
!
currentTab
.
tabId
)
{
const
current
=
await
chrome
.
tabs
.
getCurrent
()
currentTab
.
tabId
=
current
?.
id
||
-
1
}
if
(
message
.
tabId
==
currentTab
.
tabId
)
{
url
.
value
=
message
.
url
}
break
}
}
}
const
u
=
new
URL
(
url
.
value
)
async
function
updateRecentItems
(
pageInfo
:
{
if
(
!
[
"http:"
,
"https:"
].
includes
(
u
.
protocol
))
{
url
:
string
return
true
title
:
string
icon
:
string
})
{
if
(
mode
.
value
===
"content"
)
{
chrome
.
runtime
.
sendMessage
({
type
:
MessageType
.
registerContentSidebar
,
url
:
url
.
value
,
})
}
}
return
false
const
{
sidebarRecentItems
}
=
await
getLocal
({
})
sidebarRecentItems
:
[]
as
{
url
:
string
icon
:
string
title
:
string
}[],
})
const
index
=
sidebarRecentItems
.
findIndex
((
i
)
=>
i
.
url
===
pageInfo
.
url
)
if
(
index
!==
-
1
)
{
sidebarRecentItems
.
splice
(
index
,
1
)
}
sidebarRecentItems
.
unshift
(
pageInfo
)
sidebarRecentItems
.
splice
(
12
)
recentItems
.
splice
(
0
,
recentItems
.
length
,
...
sidebarRecentItems
)
await
chrome
.
storage
.
local
.
set
({
sidebarRecentItems
})
}
async
function
removeRecentItems
(
url
:
string
)
{
const
{
sidebarRecentItems
}
=
await
getLocal
({
sidebarRecentItems
:
[]
as
{
url
:
string
icon
:
string
title
:
string
}[],
})
const
index
=
sidebarRecentItems
.
findIndex
((
i
)
=>
i
.
url
===
url
)
if
(
index
!==
-
1
)
{
sidebarRecentItems
.
splice
(
index
,
1
)
}
recentItems
.
splice
(
0
,
recentItems
.
length
,
...
sidebarRecentItems
)
await
chrome
.
storage
.
local
.
set
({
sidebarRecentItems
})
}
onMounted
(()
=>
{
onMounted
(()
=>
{
const
q
=
new
URLSearchParams
(
location
.
search
)
const
q
=
new
URLSearchParams
(
location
.
search
)
const
initUrl
=
q
.
get
(
"url"
)
||
""
mode
.
value
=
q
.
get
(
"mode"
)
||
"sidepanel"
url
.
value
=
initUrl
getLocal
({
getLocal
({
popularSites
:
config
.
data
.
popularSites
,
popularSites
:
config
.
data
.
popularSites
,
...
@@ -43,6 +107,25 @@ onMounted(() => {
...
@@ -43,6 +107,25 @@ onMounted(() => {
recentItems
.
splice
(
0
,
recentItems
.
length
,
...
sidebarRecentItems
)
recentItems
.
splice
(
0
,
recentItems
.
length
,
...
sidebarRecentItems
)
}
}
})
})
getSession
({
sidebarUrls
:
{}
as
Record
<
string
,
string
>
,
}).
then
(({
sidebarUrls
})
=>
{
console
.
log
(
"[sidebar]"
,
sidebarUrls
,
mode
.
value
)
if
(
sidebarUrls
&&
sidebarUrls
[
mode
.
value
])
{
url
.
value
=
sidebarUrls
[
mode
.
value
]
}
})
chrome
.
tabs
.
getCurrent
().
then
((
t
)
=>
{
currentTab
.
tabId
=
t
?.
id
||
-
1
})
chrome
.
runtime
.
onMessage
.
addListener
(
handleMessage
)
})
onUnmounted
(()
=>
{
chrome
.
runtime
.
onMessage
.
removeListener
(
handleMessage
)
})
})
function
go
(
link
:
string
)
{
function
go
(
link
:
string
)
{
...
@@ -52,7 +135,7 @@ function go(link: string) {
...
@@ -52,7 +135,7 @@ function go(link: string) {
<
template
>
<
template
>
<div
class=
"w-full h-screen"
>
<div
class=
"w-full h-screen"
>
<Webview
v-if=
"!protectedUrl"
:url=
"url"
/>
<Webview
v-if=
"!protectedUrl"
:url=
"url"
@
page-info=
"updateRecentItems"
/>
<div
v-else
class=
"flex flex-col p-6 max-w-md mx-auto"
>
<div
v-else
class=
"flex flex-col p-6 max-w-md mx-auto"
>
<div
class=
"flex flex-col items-center gap-2 mx-auto mt-16"
>
<div
class=
"flex flex-col items-center gap-2 mx-auto mt-16"
>
...
@@ -61,45 +144,25 @@ function go(link: string) {
...
@@ -61,45 +144,25 @@ function go(link: string) {
</div>
</div>
<div
class=
"grid grid-cols-4 gap-y-4 justify-between mt-24"
>
<div
class=
"grid grid-cols-4 gap-y-4 justify-between mt-24"
>
<
b
utton
<
SiteB
utton
v-for=
"item of recentItems"
v-for=
"item of recentItems"
class=
"group w-16 shrink-0 relative flex flex-col items-center justify-between justify-self-center rounded-lg px-2 py-3 bg-background-soft"
:icon=
"item.icon"
:title=
"item.title"
badge=
"remove"
@
click=
"go(item.url)"
@
click=
"go(item.url)"
>
@
remove=
"() => removeRecentItems(item.url)"
<div
/>
class=
"size-6 rounded"
:style=
"
{
background: 'center / contain url(' + item.icon + ')',
}"
>
</div>
<div
class=
"text-xs max-w-full mt-1 break-words leading-3 line-clamp-2"
>
{{
item
.
title
}}
</div>
</button>
</div>
</div>
<!--
<div
class=
"text-center my-3"
>
Popular
</div>
-->
<!--
<div
class=
"text-center my-3"
>
Popular
</div>
-->
<div
class=
"w-full my-6 border-b border-b-slate-400/60 h-0"
></div>
<div
class=
"w-full my-6 border-b border-b-slate-400/60 h-0"
></div>
<div
class=
"grid grid-cols-4 gap-y-4 justify-between"
>
<div
class=
"grid grid-cols-4 gap-y-4 justify-between"
>
<
b
utton
<
SiteB
utton
v-for=
"item of popularItems"
v-for=
"item of popularItems"
class=
"group w-16 shrink-0 relative flex flex-col items-center justify-between justify-self-center rounded-lg px-2 py-3 bg-background-soft"
:icon=
"item.icon"
:title=
"item.title"
@
click=
"go(item.url)"
@
click=
"go(item.url)"
>
/>
<div
class=
"size-6 rounded"
:style=
"
{
background: 'center / contain url(' + item.icon + ')',
}"
>
</div>
<div
class=
"text-xs max-w-full mt-1 break-words leading-3 line-clamp-2"
>
{{
item
.
title
}}
</div>
</button>
</div>
</div>
</div>
</div>
</div>
</div>
...
...
src/pages/dev.ts
View file @
31d553e6
import
"@/content/index"
import
"@/assets/main.css"
import
"@/pages/popup"
import
{
createApp
}
from
"vue"
// import "@/content/index"
// import "@/pages/popup"
import
Dev
from
"./Dev.vue"
import
{
i18n
}
from
"@/utils/i18n"
const
app
=
createApp
(
Dev
)
app
.
use
(
i18n
)
app
.
mount
(
"#app"
)
src/store/index.ts
View file @
31d553e6
...
@@ -21,6 +21,12 @@ export const docsAddon = reactive({
...
@@ -21,6 +21,12 @@ export const docsAddon = reactive({
active
:
false
,
active
:
false
,
})
})
export
const
sidebarAddon
=
reactive
({
visible
:
false
,
hidden
:
false
,
url
:
""
,
})
type
DocInputItem
=
{
type
DocInputItem
=
{
key
:
string
key
:
string
kind
:
"file"
|
"string"
kind
:
"file"
|
"string"
...
...
src/types/chrome.ts
View file @
31d553e6
...
@@ -4,10 +4,12 @@ declare namespace chrome.declarativeNetRequest {
...
@@ -4,10 +4,12 @@ declare namespace chrome.declarativeNetRequest {
}):
Promise
<
Rule
[]
>
}):
Promise
<
Rule
[]
>
}
}
declare
interface
Manifest
extends
chrome
.
runtime
.
ManifestV3
{
declare
interface
Manifest
Patch
{
web_accessible_resources
:
Array
<
{
web_accessible_resources
:
Array
<
{
resources
:
string
[]
resources
:
string
[]
matches
:
string
[]
matches
:
string
[]
use_dynamic_url
?
:
boolean
use_dynamic_url
:
boolean
}
>
}
>
}
}
type
Manifest
=
chrome
.
runtime
.
ManifestV3
&
ManifestPatch
src/types/index.ts
View file @
31d553e6
...
@@ -5,7 +5,7 @@ export enum MessageType {
...
@@ -5,7 +5,7 @@ export enum MessageType {
bgOpenPip
=
"bg-open-pip"
,
bgOpenPip
=
"bg-open-pip"
,
bgPipLaunch
=
"bg-pip-launch"
,
bgPipLaunch
=
"bg-pip-launch"
,
pipLaunch
=
"pip-launch"
,
pipLaunch
=
"pip-launch"
,
contentMount
=
"content-mount"
,
contentMount
ed
=
"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"
,
...
@@ -16,7 +16,10 @@ export enum MessageType {
...
@@ -16,7 +16,10 @@ export enum MessageType {
invokeRequest
=
"invoke-request"
,
invokeRequest
=
"invoke-request"
,
invokeResponse
=
"invoke-Response"
,
invokeResponse
=
"invoke-Response"
,
showChatDocs
=
"show-chat-docs"
,
showChatDocs
=
"show-chat-docs"
,
frameReady
=
"frame-ready"
,
openInSidebar
=
"open-in-sidebar"
,
registerContentSidebar
=
"register-content-sidebar"
,
unregisterContentSidebar
=
"unregister-content-sidebar"
,
openContentSidebar
=
"open-content-sidebar"
,
}
}
export
enum
ServiceFunc
{
export
enum
ServiceFunc
{
...
...
src/utils/dom.ts
View file @
31d553e6
...
@@ -233,3 +233,9 @@ export function getPageIcon() {
...
@@ -233,3 +233,9 @@ export function getPageIcon() {
return
location
.
origin
+
"/favicon.ico"
return
location
.
origin
+
"/favicon.ico"
}
}
export
function
autoPointerCapture
(
e
:
PointerEvent
)
{
if
(
e
.
buttons
==
1
)
{
;(
e
.
target
as
HTMLElement
)?.
setPointerCapture
(
e
.
pointerId
)
}
}
src/utils/ext.ts
View file @
31d553e6
...
@@ -237,3 +237,28 @@ export async function updateFrameNetRules(
...
@@ -237,3 +237,28 @@ export async function updateFrameNetRules(
],
],
})
})
}
}
export
function
isProtectedUrl
(
url
:
string
)
{
try
{
const
u
=
new
URL
(
url
)
if
(
!
[
"http:"
,
"https:"
].
includes
(
u
.
protocol
))
{
return
true
}
const
isEdge
=
/Edg/
.
test
(
navigator
.
userAgent
)
if
(
isEdge
&&
u
.
hostname
==
"microsoftedge.microsoft.com"
)
{
return
true
}
if
(
u
.
hostname
==
"chrome.google.com"
)
{
return
true
}
return
false
}
catch
(
e
)
{
console
.
warn
(
e
)
}
return
true
}
src/utils/utils.ts
View file @
31d553e6
...
@@ -51,7 +51,8 @@ export const semanticClip = (text: string, maxLength: number) => {
...
@@ -51,7 +51,8 @@ export const semanticClip = (text: string, maxLength: number) => {
return
text
.
slice
(
0
,
breakPoint
)
return
text
.
slice
(
0
,
breakPoint
)
}
}
export
async
function
findFrameLoadUrl
(
urls
:
string
[])
{
/** Find URL from the same origin that can be embedded */
export
async
function
findFrameLoadUrl
(
urls
:
string
[]):
Promise
<
string
>
{
const
abortController
=
new
AbortController
()
const
abortController
=
new
AbortController
()
let
resolve
:
null
|
((
url
:
string
)
=>
void
)
=
null
let
resolve
:
null
|
((
url
:
string
)
=>
void
)
=
null
...
...
vite.config.ts
View file @
31d553e6
...
@@ -59,6 +59,7 @@ export default defineConfig({
...
@@ -59,6 +59,7 @@ export default defineConfig({
outDir
:
"dist"
,
outDir
:
"dist"
,
rollupOptions
:
{
rollupOptions
:
{
input
:
{
input
:
{
// dev: "dev.html",
offscreen
:
"offscreen.html"
,
offscreen
:
"offscreen.html"
,
},
},
output
:
{
output
:
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment