diff --git a/checksum b/checksum index ae65ac6..afce377 100644 --- a/checksum +++ b/checksum @@ -1,3 +1,3 @@ 1af97e7578a6b23af1aff33269acf462 public/rime.data -d0996b971d3fd9ddb9b1817b48553037 public/rime.js -e77712e8aff7bd886fb3b647282e98ce public/rime.wasm +d9c90378abd6d13b11bc136f67e72961 public/rime.js +340f2b7c06f793751ec1d28d21479274 public/rime.wasm diff --git a/scripts/build_wasm.mjs b/scripts/build_wasm.mjs index d993052..85410fc 100644 --- a/scripts/build_wasm.mjs +++ b/scripts/build_wasm.mjs @@ -9,11 +9,22 @@ const RIME_PATH = 'build/librime_native/bin' const OPENCC_HOST = `${RIME_PATH}/opencc` const RIME_SHARED = '/usr/share/rime-data' +const exportedFunctions = [ + 'init', + 'set_schema_name', + 'set_option', + 'set_ime', + 'process', + 'select_candidate_on_current_page', + 'deploy', + 'unset_deployed' +] + const compileArgs = [ '-std=c++14', '-O2', '-s', 'ALLOW_MEMORY_GROWTH=1', - '-s', 'EXPORTED_FUNCTIONS=_init,_set_schema_name,_set_option,_set_ime,_process,_select_candidate_on_current_page,_deploy', + '-s', `EXPORTED_FUNCTIONS=${exportedFunctions.map(name => '_' + name).join(',')}`, '-s', 'EXPORTED_RUNTIME_METHODS=["ccall","FS"]', '--preload-file', `${OPENCC_HOST}@${OPENCC_TARGET}`, '--preload-file', `${RIME_PATH}/build/default.yaml@${RIME_SHARED}/build/default.yaml`, diff --git a/src/components/MyDeployer.vue b/src/components/MyDeployer.vue index 22e75d3..179e36d 100644 --- a/src/components/MyDeployer.vue +++ b/src/components/MyDeployer.vue @@ -7,6 +7,7 @@ import { selectOptions, setLoading } from '../control' +import { getQueryOrStoredString } from '../util' const notification = useNotification() @@ -48,7 +49,8 @@ worker.control('deployStatus', async (status: 'start' | 'failure' | 'success', s value: schema.id }) } - selectIME(schemasJson[0].id) + const schemaId = getQueryOrStoredString('schemaId') + selectIME(schemasJson.map(schema => schema.id).includes(schemaId) ? schemaId : schemasJson[0].id) } break } diff --git a/src/components/MyMenu.vue b/src/components/MyMenu.vue index 43cfd57..c184d17 100644 --- a/src/components/MyMenu.vue +++ b/src/components/MyMenu.vue @@ -34,8 +34,12 @@ const variantLabel = computed(() => showVariant.value && !deployed.value ? varia const singleVariant = computed(() => !deployed.value && variants.value.length === 1) watchEffect(() => { - localStorage.setItem('schemaId', ime.value) - localStorage.setItem('variantName', variantLabel.value) + if (ime.value) { + localStorage.setItem('schemaId', ime.value) + } + if (variantLabel.value) { + localStorage.setItem('variantName', variantLabel.value) + } }) async function switchVariant () { diff --git a/src/components/micro-plum/MicroPlum.vue b/src/components/micro-plum/MicroPlum.vue index 4ca2c41..ea36a1f 100644 --- a/src/components/micro-plum/MicroPlum.vue +++ b/src/components/micro-plum/MicroPlum.vue @@ -11,9 +11,7 @@ import { import { resetUserDirectory } from '../../workerAPI' import { init, - deployed, - selectOptions, - defaultSelectOptions + setLoading } from '../../control' import InstallPane from './InstallPane.vue' import DeployPane from './DeployPane.vue' @@ -70,9 +68,9 @@ function showMicroPlum () { } async function onReset () { + setLoading(true) + localStorage.removeItem('schemaId') await resetUserDirectory() - deployed.value = false - selectOptions.value = defaultSelectOptions init() } diff --git a/src/control.ts b/src/control.ts index 9f42679..20d988c 100644 --- a/src/control.ts +++ b/src/control.ts @@ -1,6 +1,15 @@ import { computed, ref, Ref } from 'vue' -import { setOption, setIME } from './workerAPI' -import { getQueryString } from './util' +import { + setOption, + setIME, + resetUserDirectory, + FS, + deploy +} from './workerAPI' +import { + getQueryString, + getQueryOrStoredString +} from './util' import schemas from '../schemas.json' const ASCII_MODE = 'ascii_mode' @@ -18,6 +27,7 @@ const ime = ref('') // visual vs internal const loading = ref(true) function setLoading (value: boolean) { + showVariant.value = !value loading.value = value ime.value = value ? '' : schemaId.value } @@ -50,7 +60,7 @@ const defaultSelectOptions: ( }) )[] = [] -const selectOptions = ref(defaultSelectOptions) +const selectOptions = ref([]) type Variants = { id: string, @@ -170,9 +180,26 @@ const variantIndex = computed({ const variant = computed(() => variants.value[variantIndex.value]) +async function hasUserDefaultYaml () { + try { + await FS.lstat('/rime/build/default.yaml') + return true + } catch { + return false + } +} + async function init () { - const _schemaId = getQueryString('schemaId') - const variantName = getQueryString('variantName') + if (getQueryString('schemaId') in schemaVariants) { + await resetUserDirectory() + } + if (await hasUserDefaultYaml()) { + return deploy() + } + const _schemaId = getQueryOrStoredString('schemaId') + const variantName = getQueryOrStoredString('variantName') + selectOptions.value = defaultSelectOptions + deployed.value = false schemaId.value = _schemaId in schemaVariants ? _schemaId : schemas[0].id variantIndex.value = 0 for (let i = 0; i < variants.value.length; ++i) { @@ -226,7 +253,6 @@ function changeVariant () { } async function selectIME (targetIME: string) { - showVariant.value = false setLoading(true) try { await setIME(targetIME) @@ -247,7 +273,6 @@ async function selectIME (targetIME: string) { } catch (e) { console.error(e) } - showVariant.value = true setLoading(false) } diff --git a/src/util.ts b/src/util.ts index 2cd3634..166be32 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,5 @@ import { computed } from 'vue' -import { useRoute, LocationQuery } from 'vue-router' +import { LocationQuery } from 'vue-router' import { useBreakpoint } from 'vooks' let query: LocationQuery @@ -15,21 +15,19 @@ function getTextarea (selector: string) { return document.querySelector(selector) as HTMLTextAreaElement } -let route: ReturnType | undefined - function getQueryString (key: string) { - console.log(query) - if (!route) { - route = useRoute() - } - const queryValue = route.query[key] - const param = typeof queryValue === 'string' ? queryValue : '' - return param || localStorage.getItem(key) || '' + const queryValue = query[key] + return typeof queryValue === 'string' ? queryValue : '' +} + +function getQueryOrStoredString (key: string) { + return getQueryString(key) || localStorage.getItem(key) || '' } export { isMobile, setQuery, getTextarea, - getQueryString + getQueryString, + getQueryOrStoredString } diff --git a/src/worker.ts b/src/worker.ts index 24f863a..75774e5 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -69,6 +69,7 @@ async function setIME (schemaId: string) { await fetchPrebuilt(schemaId) } Module.ccall('set_ime', 'null', ['string'], [schemaId]) + return syncUserDirectory('write') } function syncUserDirectory (direction: 'read' | 'write') { @@ -130,6 +131,7 @@ async function resetUserDirectory () { rmStar(RIME_USER) await syncUserDirectory('write') deployed = false + Module.ccall('unset_deployed', 'null', [], []) } expose({ diff --git a/test/test-advanced.spec.ts b/test/test-advanced.spec.ts index 787036c..6a5f681 100644 --- a/test/test-advanced.spec.ts +++ b/test/test-advanced.spec.ts @@ -9,7 +9,8 @@ import { item, selectIME, chain, - callOnDownload + callOnDownload, + luna } from './util' function deploy (page: Page) { @@ -28,6 +29,7 @@ async function upload (page: Page, files: string[]) { test('Debug', async ({ page }) => { await page.goto(debugURL) + await expect(select(page)).toHaveText(luna) const debugInput = page.locator('input') diff --git a/test/test-micro-plum.spec.ts b/test/test-micro-plum.spec.ts index 8e8d94d..bbe10bd 100644 --- a/test/test-micro-plum.spec.ts +++ b/test/test-micro-plum.spec.ts @@ -1,4 +1,4 @@ -import { test, expect, Page } from '@playwright/test' +import { test, expect, Page, Locator } from '@playwright/test' import { expectValue, init, @@ -23,7 +23,15 @@ function microPlum (page: Page) { return page.locator('.n-dialog') } -test('Install from URL', async ({ page }) => { +async function installAndDeploy (page: Page, mp: Locator, ime: string) { + await mp.getByRole('button').getByText('Install').click() + await mp.getByRole('button').getByText('Deploy').click() + await expect(page.getByText('Deployment succeeded')).toBeVisible() + await expect(variantButton(page)).toBeDisabled() + return newIMEReady(page, ime) +} + +test('Install from URL, then reload with query string', async ({ page }) => { const url = 'https://github.com/lotem/rime-zhengma/blob/master/zhengma.schema.yaml' const ime = '郑码' const sequence = 'uggx' @@ -34,13 +42,58 @@ test('Install from URL', async ({ page }) => { const mp = microPlum(page) await mp.getByPlaceholder('GitHub URL of *.schema.yaml').click() await page.keyboard.insertText(url) - await mp.getByRole('button').getByText('Install').click() - await mp.getByRole('button').getByText('Deploy').click() - await expect(page.getByText('Deployment succeeded')).toBeVisible() - await expect(variantButton(page)).toBeDisabled() + await installAndDeploy(page, mp, ime) + await input(page, sequence, ' ') + await expectValue(page, text) + + await page.reload() await newIMEReady(page, ime) await input(page, sequence, ' ') await expectValue(page, text) + + await init(page, '粤语拼音', 'jyut6ping3', '港') + await input(page, 'saan', 'fung ', 'wai', 'ho ', 'syut', 'waa ') + await expectValue(page, '山峯為何説話') + await page.keyboard.press('F4') + await expect(item(page, '4 朙月拼音·语句流')).toBeVisible() +}) + +test('Install from plum, then reset', async ({ page }) => { + const target = 'ipa' + const schemas = ['ipa_xsampa', 'ipa_yunlong'] + const names = ['X-SAMPA', '雲龍國際音標'] + const sequence = 'jj' + const text = 'ʝ' + await init(page) + + await page.getByText('Micro Plum').click() + const mp = microPlum(page) + await mp.getByLabel('jsDelivr').click() + await mp.getByLabel('Plum').click() + await mp.getByPlaceholder('rime-luna-pinyin').click() + await page.keyboard.insertText(target) + for (const schema of schemas) { + await mp.getByText('luna_pinyin').click() + await page.keyboard.insertText(schema) + await mp.getByText('Schemas').click() + } + await installAndDeploy(page, mp, names[0]) + await select(page).click() + await expect(page.locator('.n-base-select-option')).toHaveCount(names.length) + await page.getByText(names[1]).click() + await expect(select(page)).toHaveText(names[1]) + await input(page, sequence, ' ') + await expectValue(page, text) + + await page.reload() + await newIMEReady(page, names[1]) + await input(page, sequence, ' ') + await expectValue(page, text) + + await page.getByText('Reset').click() + await newIMEReady(page, luna) + await input(page, 'jian', 'ti ') + await expectValue(page, 'ʝ简体') }) test('Reset frequency', async ({ page }) => { diff --git a/wasm/api.cpp b/wasm/api.cpp index e1f2fdb..cdbb320 100644 --- a/wasm/api.cpp +++ b/wasm/api.cpp @@ -9,6 +9,7 @@ namespace my_rime { enum { COMMITTED, ACCEPTED, REJECTED, UNHANDLED }; +RimeTraits traits = {0}; RimeSessionId session_id; RimeCommit commit; RimeContext context; @@ -56,9 +57,6 @@ void handler(void *context_object, RimeSessionId session_id, std::string get_schema_name(std::string schema) { return schema_name[schema]; } void startRime() { - RIME_STRUCT(RimeTraits, traits); - traits.user_data_dir = "/rime"; - traits.shared_data_dir = "/usr/share/rime-data"; RimeInitialize(&traits); RimeSetNotificationHandler(handler, NULL); } @@ -129,7 +127,10 @@ void set_option(const char *option, int value) { } void init() { - RimeSetup(NULL); + RIME_STRUCT_INIT(RimeTraits, traits); + traits.user_data_dir = "/rime"; + traits.shared_data_dir = "/usr/share/rime-data"; + RimeSetup(&traits); startRime(); session_id = RimeCreateSession(); RIME_STRUCT_INIT(RimeCommit, commit); @@ -169,6 +170,8 @@ void deploy() { RimeStartMaintenance(true); session_id = RimeCreateSession(); } + +void unset_deployed() { deployed = false; } } } // namespace my_rime