Skip to content

Commit

Permalink
RPPI integration
Browse files Browse the repository at this point in the history
  • Loading branch information
eagleoflqj committed Jul 30, 2023
1 parent 0382970 commit 51b9d44
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 10 deletions.
16 changes: 8 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
},
"devDependencies": {
"@codemirror/legacy-modes": "^6.3.3",
"@libreservice/lazy-cache": "^0.1.0",
"@libreservice/micro-plum": "^0.1.5",
"@libreservice/lazy-cache": "^0.1.1",
"@libreservice/micro-plum": "^0.1.8",
"@libreservice/my-widget": "^0.1.4",
"@libreservice/my-worker": "^0.4.2",
"@libreservice/wasm-code": "^0.1.2",
Expand All @@ -52,18 +52,18 @@
"@vitejs/plugin-vue": "^4.2.3",
"client-zip": "^2.4.4",
"emoji-regex": "^10.2.1",
"esbuild": "^0.18.16",
"eslint": "^8.45.0",
"esbuild": "^0.18.17",
"eslint": "^8.46.0",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-import": "^2.28.0",
"eslint-plugin-n": "^16.0.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-vue": "^9.15.1",
"eslint-plugin-vue": "^9.16.0",
"idb": "^7.1.1",
"js-yaml": "^4.1.0",
"luaparse": "^0.3.1",
"naive-ui": "^2.34.4",
"rollup": "^3.26.3",
"rollup": "^3.27.0",
"rollup-plugin-esbuild": "^5.0.0",
"textarea-caret": "^3.1.0",
"tslib": "^2.6.1",
Expand All @@ -74,6 +74,6 @@
"vooks": "^0.2.12",
"vue": "^3.3.4",
"vue-router": "^4.2.4",
"vue-tsc": "^1.8.6"
"vue-tsc": "^1.8.8"
}
}
232 changes: 232 additions & 0 deletions src/components/micro-plum/ManifestPane.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
<script setup lang="ts">
import { ref, h } from 'vue'
import { NForm, NFormItem, NInput, NSpace, NTreeSelect, TreeSelectOption, NButton, NTag, NText, useMessage } from 'naive-ui'
import { LazyCache } from '@libreservice/lazy-cache'
import {
prerequisites,
install
} from '../../micro-plum'
import {
RPPI,
_rppi,
tab,
downloading,
installedPrerequisites
} from './MicroPlum.vue'
let rppi = _rppi.value
const INDEX = 'index.json'
const lazyCache = new LazyCache('rppi')
const decoder = new TextDecoder('utf-8', { fatal: true })
let date = ''
const labelMap = {
chord: '并击',
lua: 'Lua'
}
const loadingIndex = ref<boolean>(false)
type PARENT_INDEX = {
categories: {
key: string
name: string
}[]
}
type RECIPE = {
repo: string
branch?: string
name: string
labels?: (keyof typeof labelMap)[]
schemas: string[]
dependencies?: string[]
license?: string
}
type CHILD_INDEX = {
recipes: RECIPE[]
}
const props = defineProps<{
message: ReturnType<typeof useMessage>
}>()
const repoKeyMap: {
[key: string]: string
} = {}
const keyRecipeMap: {
[key: string]: RECIPE
} = {}
const rules = {
rppi: {
trigger: ['blur'],
validator: async () => {
let error
if (!/^https?:\/\/\S*\/index.json$/.test(_rppi.value)) {
if (_rppi.value) {
error = new Error('Invalid RPPI source')
}
_rppi.value = RPPI
}
if (rppi !== _rppi.value) {
rppi = _rppi.value
await lazyCache.invalidate()
await updateIndex()
}
if (error) {
throw error
}
}
}
}
const selectedKey = ref<string | undefined>()
async function fetchJson (key: string, time: string) {
const url = `${rppi.slice(0, rppi.lastIndexOf('/'))}/${key}`
const ab = await lazyCache.get(key, time, url)
return JSON.parse(decoder.decode(new Uint8Array(ab)))
}
const options = ref<TreeSelectOption[]>([])
async function handleLoad (option: TreeSelectOption) {
const index = await fetchJson(`${option.key}/${INDEX}`, date) as PARENT_INDEX | CHILD_INDEX
if ('categories' in index) {
option.children = await Promise.all(index.categories.map(async category => {
const subOption = {
label: category.name,
key: `${option.key}/${category.key}`,
isLeaf: false
}
await handleLoad(subOption)
return subOption
}))
} else {
option.children = []
for (const recipe of index.recipes) {
const key = `${option.key}/${recipe.repo}`
repoKeyMap[recipe.repo] = key
keyRecipeMap[key] = recipe
option.children.push({
label: recipe.name,
key,
isLeaf: true
})
}
}
}
function filterRepo (pattern: string, option: TreeSelectOption) {
return (option.key as string).toLowerCase().includes(pattern.toLowerCase())
}
async function updateIndex () {
loadingIndex.value = true
const _options = []
options.value = []
try {
const index = await fetchJson(INDEX, Math.round(new Date().getTime() / 86400000).toString()) as {
date: string
} & PARENT_INDEX // cache for today
date = index.date
for (const category of index.categories) {
const option = {
label: category.name,
key: category.key,
isLeaf: false
}
await handleLoad(option)
_options.push(option)
}
options.value = _options
} finally {
loadingIndex.value = false
}
}
updateIndex()
async function onClick () {
downloading.value = true
try {
if (!installedPrerequisites.value) {
await Promise.all(prerequisites.map(prerequisite => install(prerequisite)))
installedPrerequisites.value = true
}
const { repo, branch, schemas, dependencies } = keyRecipeMap[selectedKey.value!]
if (dependencies) {
await Promise.all(dependencies.map(dependency => {
const recipe = keyRecipeMap[repoKeyMap[dependency]]
const target = recipe.branch ? `${recipe.repo}@${recipe.branch}` : recipe.repo
return install(target, { schemaIds: recipe.schemas })
}))
}
const target = branch ? `${repo}@${branch}` : repo
await install(target, { schemaIds: schemas })
tab.value = 'deploy'
} catch (e) {
console.error(e)
props.message.error((e as Error).message)
}
downloading.value = false
}
function renderSuffix (info: { option: TreeSelectOption }) {
if (info.option.key as string in keyRecipeMap) {
const nodes: ReturnType<typeof h>[] = []
const { repo, labels, license } = keyRecipeMap[info.option.key as string]
labels?.forEach(label => nodes.push(h(NTag, {
type: 'info',
size: 'small',
bordered: false
}, () => [labelMap[label] || label])))
nodes.push(h(NText, {
type: license ? 'success' : 'warning',
title: license ? 'Free' : 'Proprietary'
}, () => repo))
return h(NSpace, {}, () => nodes)
}
}
</script>

<template>
<n-form
:disabled="downloading"
:rules="rules"
>
<n-form-item
label="RPPI source"
path="rppi"
>
<n-input
v-model:value="_rppi"
clearable
/>
</n-form-item>
<n-form-item label="Schema">
<n-tree-select
v-model:value="selectedKey"
:loading="loadingIndex"
check-strategy="child"
filterable
:filter="filterRepo"
:options="options"
:render-suffix="renderSuffix"
/>
</n-form-item>
</n-form>
<div style="display: flex; justify-content: end">
<n-button
secondary
type="info"
:disabled="!selectedKey || downloading"
@click="onClick"
>
Install
</n-button>
</div>
</template>
40 changes: 38 additions & 2 deletions src/components/micro-plum/MicroPlum.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,24 @@ import {
init,
setLoading
} from '../../control'
import ManifestPane from './ManifestPane.vue'
import InstallPane from './InstallPane.vue'
import DeployPane from './DeployPane.vue'
const tab = ref<'install' | 'deploy'>('install')
const RPPI = 'https://raw.githubusercontent.com/LibreService/rppi/HEAD/index.json'
const _rppi = ref<string>(RPPI)
const tab = ref<'rppi' | 'install' | 'deploy'>('install')
const source = ref<'GitHub' | 'jsDelivr'>('GitHub')
const mode = ref<'schema' | 'plum'>('schema')
const installedPrerequisites = ref<boolean>(false)
const downloading = ref<boolean>(false)
const preSelectedSchemas = ref<string[]>([])
export {
RPPI,
_rppi,
tab,
source,
mode,
Expand All @@ -34,10 +41,32 @@ export {
</script>

<script setup lang="ts">
const dialog = useDialog()
const message = useMessage()
async function showRPPI () {
tab.value = 'rppi'
const dialogInstance = dialog.info({
title: 'RPPI',
content: () => h(NTabs, {
type: 'segment',
value: tab.value,
'onUpdate:value': newValue => { tab.value = newValue }
}, () => [
h(NTabPane, {
name: 'rppi',
tab: 'RPPI'
}, () => [h(ManifestPane, { message })]),
h(NTabPane, {
name: 'deploy',
tab: 'Deploy'
}, () => [h(DeployPane, {
dialogInstance
})])
])
})
}
function showMicroPlum () {
tab.value = 'install'
preSelectedSchemas.value = []
Expand Down Expand Up @@ -74,6 +103,13 @@ async function onReset () {
<template>
<n-space style="align-items: center">
<h3>Add new schemas</h3>
<n-button
secondary
type="info"
@click="showRPPI"
>
RPPI
</n-button>
<n-button
secondary
type="success"
Expand Down

0 comments on commit 51b9d44

Please sign in to comment.