Skip to content

Commit

Permalink
support auto copy to clipboard
Browse files Browse the repository at this point in the history
  • Loading branch information
eagleoflqj committed Jul 28, 2023
1 parent 2e3ffb6 commit 0382970
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 90 deletions.
76 changes: 76 additions & 0 deletions src/components/MyBar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<script setup lang="ts">
import { NSpace, NButtonGroup, NButton, NIcon, NCheckbox } from 'naive-ui'
import {
Cut20Regular,
Copy20Regular,
ClipboardLink20Regular
} from '@vicons/fluent'
import { getTextarea } from '../util'
import {
text,
loading,
deployed,
autoCopy,
schemaId,
variant
} from '../control'
function copy () {
const textarea = getTextarea()
textarea.focus()
return navigator.clipboard.writeText(text.value)
}
async function cut () {
await copy()
text.value = ''
}
async function copyLink () {
const usp = new URLSearchParams({
schemaId: schemaId.value,
variantName: variant.value.name
})
const url = `${window.location.origin}${window.location.pathname}?${usp}`
await navigator.clipboard.writeText(url)
const textarea = getTextarea()
textarea.focus()
}
</script>

<template>
<n-space style="align-items: center">
<n-button-group class="square-group">
<n-button
secondary
@click="cut"
>
<n-icon :component="Cut20Regular" />
</n-button>
<n-button
secondary
@click="copy"
>
<n-icon :component="Copy20Regular" />
</n-button>
<n-button
:disabled="loading || deployed"
secondary
title="Copy link for current IME"
@click="copyLink"
>
<n-icon :component="ClipboardLink20Regular" />
</n-button>
</n-button-group>
<!-- Least astonishment: user may explicitly cut, so shouldn't overwrite the clipboard. -->
<n-checkbox v-model:checked="autoCopy">
Auto copy on commit
</n-checkbox>
</n-space>
</template>

<style scoped>
.n-button-group .n-button {
font-size: 24px;
}
</style>
6 changes: 1 addition & 5 deletions src/components/MyMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,8 @@ async function switchVariant () {
const extendedDisabled = computed(() => ime.value !== schemaId.value || !schemaExtended.includes(ime.value))
const props = defineProps<{
textareaSelector: string
}>()
function resetFocus () {
getTextarea(props.textareaSelector).focus()
getTextarea().focus()
}
function onSelectIME (value: string) {
Expand Down
24 changes: 12 additions & 12 deletions src/components/MyPanel.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { nextTick, ref, toRefs, toRaw, onMounted, onUnmounted, watch } from 'vue'
import { nextTick, ref, toRefs, onMounted, onUnmounted, watch } from 'vue'
import { NPopover, NMenu, MenuOption, NText, NButton, NIcon } from 'naive-ui'
import { CaretLeft, CaretRight } from '@vicons/fa'
// @ts-ignore
Expand All @@ -10,6 +10,8 @@ import {
selectCandidateOnCurrentPage
} from '../workerAPI'
import {
text,
autoCopy,
forceVertical,
loading,
hideComment,
Expand All @@ -20,15 +22,10 @@ import {
import { isMobile, getTextarea } from '../util'
const props = defineProps<{
textareaSelector: string
text: string
debugMode?: boolean
updateText:(newText: string) => void
}>()
const { textareaSelector, updateText } = toRaw(props)
const {
text,
debugMode
} = toRefs(props)
Expand Down Expand Up @@ -125,16 +122,19 @@ function isEmoji (c: string) {
}
function insert (toInsert: string) {
const textarea = getTextarea(textareaSelector)
const textarea = getTextarea()
const { selectionStart, selectionEnd } = textarea
updateText(text.value.slice(0, selectionStart) + toInsert + text.value.slice(selectionEnd))
text.value = text.value.slice(0, selectionStart) + toInsert + text.value.slice(selectionEnd)
if (autoCopy.value) {
navigator.clipboard.writeText(text.value)
}
nextTick(() => {
textarea.selectionEnd = selectionStart + toInsert.length
})
}
async function analyze (result: RIME_RESULT, rimeKey: string) {
const textarea = getTextarea(textareaSelector)
const textarea = getTextarea()
if (!('updatedSchema' in result) && result.updatedOptions) {
syncOptions(result.updatedOptions)
}
Expand Down Expand Up @@ -201,8 +201,8 @@ watch(text, (acNewText, acText) => {
if (acText.length + 1 === acNewText.length &&
acText.substring(0, acStart) === acNewText.substring(0, acStart) &&
acText.substring(acEnd) === acNewText.substring(acEnd + 1)) {
const textarea = getTextarea(textareaSelector)
updateText(acText)
const textarea = getTextarea()
text.value = acText
nextTick(() => {
editing.value = true
textarea.selectionEnd = acStart
Expand All @@ -217,7 +217,7 @@ function onKeydown (e: KeyboardEvent) {
return
}
const { code, key } = e
const textarea = getTextarea(textareaSelector)
const textarea = getTextarea()
// begin: code specific to Android Chromium
if (key === 'Unidentified') {
androidChromium = true
Expand Down
7 changes: 7 additions & 0 deletions src/control.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import {
getAvailableSchemas
} from './micro-plum'

const text = ref<string>('')

const ASCII_MODE = 'ascii_mode'
const FULL_SHAPE = 'full_shape'
const EXTENDED_CHARSET = 'extended_charset'
Expand All @@ -36,6 +38,9 @@ function savedBooleanRef (key: string, initial: boolean) {
return box
}

const AUTO_COPY = 'autoCopy'
const autoCopy = savedBooleanRef(AUTO_COPY, false)

const FORCE_VERTICAL = 'forceVertical'
const forceVertical = savedBooleanRef(FORCE_VERTICAL, false)

Expand Down Expand Up @@ -379,7 +384,9 @@ function syncOptions (updatedOptions: string[]) {

export {
init,
text,
deployed,
autoCopy,
forceVertical,
loading,
schemaId,
Expand Down
6 changes: 4 additions & 2 deletions src/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ function setQuery (_query: LocationQuery) {
const breakpoint = useBreakpoint()
const isMobile = computed(() => breakpoint.value === 'xs')

function getTextarea (selector: string) {
return document.querySelector(selector) as HTMLTextAreaElement
const textareaSelector = '#container textarea'

function getTextarea () {
return document.querySelector(textareaSelector) as HTMLTextAreaElement
}

function getQueryString (key: string) {
Expand Down
74 changes: 7 additions & 67 deletions src/views/MainView.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<script setup lang="ts">
import { ref, computed, watch, defineAsyncComponent } from 'vue'
import { useRoute } from 'vue-router'
import { NInput, NSpace, NButtonGroup, NButton, NIcon, NSwitch, useMessage } from 'naive-ui'
import { Cut20Regular, Copy20Regular, ClipboardLink20Regular } from '@vicons/fluent'
import { NInput, NSpace, NSwitch, useMessage } from 'naive-ui'
import MyMenu from '../components/MyMenu.vue'
import MyPanel from '../components/MyPanel.vue'
import MyBar from '../components/MyBar.vue'
import MyAppearance from '../components/MyAppearance.vue'
import MyFont from '../components/MyFont.vue'
import MyDeployer from '../components/MyDeployer.vue'
Expand All @@ -20,58 +20,29 @@ import {
} from '../util'
import {
init,
schemaId,
variant
text
} from '../control'
import { setMessage } from '../micro-plum'
setQuery(useRoute().query)
setMessage(useMessage())
init()
const textareaSelector = '#container textarea'
let savedStart = 0
let savedEnd = 0
const text = ref<string>('')
function updateText (newText: string) {
text.value = newText
}
function onBlur () {
const textarea = getTextarea(textareaSelector)
const textarea = getTextarea()
savedStart = textarea.selectionStart
savedEnd = textarea.selectionEnd
}
function onFocus () {
const textarea = getTextarea(textareaSelector)
const textarea = getTextarea()
textarea.selectionStart = savedStart
textarea.selectionEnd = savedEnd
}
function copy () {
const textarea = getTextarea(textareaSelector)
textarea.focus()
return navigator.clipboard.writeText(text.value)
}
async function cut () {
await copy()
text.value = ''
}
async function copyLink () {
const usp = new URLSearchParams({
schemaId: schemaId.value,
variantName: variant.value.name
})
const url = `${window.location.origin}${window.location.pathname}?${usp}`
await navigator.clipboard.writeText(url)
const textarea = getTextarea(textareaSelector)
textarea.focus()
}
const panel = ref<InstanceType<typeof MyPanel>>()
const simulator = ref<InstanceType<typeof MySimulator>>()
const editor = ref<InstanceType<typeof MyEditor>>()
Expand Down Expand Up @@ -101,9 +72,7 @@ const AsyncEditor = defineAsyncComponent(() => import('../components/MyEditor.vu
vertical
class="my-column"
>
<my-menu
:textarea-selector="textareaSelector"
/>
<my-menu />
<n-input
id="container"
v-model:value="text"
Expand All @@ -113,32 +82,9 @@ const AsyncEditor = defineAsyncComponent(() => import('../components/MyEditor.vu
@blur="onBlur"
@focus="onFocus"
/>
<n-button-group class="square-group">
<n-button
secondary
@click="cut"
>
<n-icon :component="Cut20Regular" />
</n-button>
<n-button
secondary
@click="copy"
>
<n-icon :component="Copy20Regular" />
</n-button>
<n-button
secondary
title="Copy link for current IME"
@click="copyLink"
>
<n-icon :component="ClipboardLink20Regular" />
</n-button>
</n-button-group>
<my-bar />
<my-panel
ref="panel"
:textarea-selector="textareaSelector"
:text="text"
:update-text="updateText"
:debug-mode="simulator?.debugMode"
/>
<my-appearance />
Expand Down Expand Up @@ -168,9 +114,3 @@ const AsyncEditor = defineAsyncComponent(() => import('../components/MyEditor.vu
<my-platform />
</n-space>
</template>
<style scoped>
.n-button-group .n-button {
font-size: 24px;
}
</style>
24 changes: 20 additions & 4 deletions test/test-basic.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { test, Request, expect } from '@playwright/test'
import { test, Request, Page, expect } from '@playwright/test'
import {
baseURL,
browserName,
Expand Down Expand Up @@ -329,6 +329,10 @@ test('IndexedDB cache', async ({ page }) => {
await Promise.race([expectValue(page, '缓存'), promise])
})

async function expectClipboard (page: Page, text: string) {
while (await page.evaluate(() => navigator.clipboard.readText()) !== text);
}

test('Cut button', async ({ page }) => {
test.skip(browserName(page) !== 'chromium')
await page.context().grantPermissions(['clipboard-read', 'clipboard-write'])
Expand All @@ -337,7 +341,7 @@ test('Cut button', async ({ page }) => {
await input(page, 'jian', 'qie ')
await expectValue(page, '剪切')
await cut(page)
while (await page.evaluate(() => navigator.clipboard.readText()) !== '剪切');
await expectClipboard(page, '剪切')
await expectValue(page, '')
})

Expand All @@ -350,7 +354,7 @@ test('Copy button', async ({ page }) => {
await expectValue(page, '复制')
await copy(page)
await expect(textarea(page)).toBeFocused()
while (await page.evaluate(() => navigator.clipboard.readText()) !== '复制');
await expectClipboard(page, '复制')
})

test('Copy link button', async ({ page }) => {
Expand All @@ -362,7 +366,19 @@ test('Copy link button', async ({ page }) => {
await copyLink(page)
await expect(textarea(page)).toBeFocused()
const copiedURL = `${baseURL}?schemaId=luna_pinyin&variantName=%E7%B9%81`
while (await page.evaluate(() => navigator.clipboard.readText()) !== copiedURL);
await expectClipboard(page, copiedURL)
})

test('Auto copy on commit', async ({ page }) => {
test.skip(browserName(page) !== 'chromium')
await page.context().grantPermissions(['clipboard-read', 'clipboard-write'])
await init(page)

await page.getByLabel('Auto copy on commit').click()
await textarea(page).click()
await input(page, 'fu', 'zhi ')
await expectValue(page, '复制')
await expectClipboard(page, '复制')
})

test('Lua', async ({ page }) => {
Expand Down

0 comments on commit 0382970

Please sign in to comment.