improve native isolation
This commit is contained in:
parent
3f57682a9b
commit
9748b98163
15 changed files with 341 additions and 95 deletions
|
|
@ -335,11 +335,24 @@ class CustomSelect {
|
|||
if (this.disabled) return
|
||||
const wasOpen = this.host.classList.contains('open')
|
||||
CustomSelect.closeAll()
|
||||
if (!wasOpen) this.host.classList.add('open')
|
||||
if (!wasOpen) {
|
||||
this.host.classList.add('open')
|
||||
this.lift()
|
||||
}
|
||||
}
|
||||
|
||||
// the transform on every element (see style.css) makes each one its own
|
||||
// stacking context, so an ancestor wrapper would still paint the open list
|
||||
// under the rows that follow it; positioned ancestors already stack right
|
||||
lift() {
|
||||
for (let el = this.host.parentElement; el && el !== document.body; el = el.parentElement) {
|
||||
if (getComputedStyle(el).position === 'static') el.classList.add('select-lift')
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
this.host.classList.remove('open')
|
||||
CustomSelect.unlift()
|
||||
}
|
||||
|
||||
setValue(v) {
|
||||
|
|
@ -358,6 +371,11 @@ class CustomSelect {
|
|||
|
||||
static closeAll() {
|
||||
qsa('.custom-select.open').forEach(el => el.classList.remove('open'))
|
||||
CustomSelect.unlift()
|
||||
}
|
||||
|
||||
static unlift() {
|
||||
qsa('.select-lift').forEach(el => el.classList.remove('select-lift'))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -434,12 +452,21 @@ function isolationStatusText(status) {
|
|||
return t('config.isolation.env', t('sandbox.reason.' + (status.reason || 'failed')))
|
||||
}
|
||||
|
||||
function isolationBadge(game) {
|
||||
// The badge names the level the app would really run at, not the policy:
|
||||
// automatic is a choice, and what it resolves to depends on this machine.
|
||||
function isolationLevel(game) {
|
||||
const policy = (game && game.isolation) || 'auto'
|
||||
if (policy === 'off') return `<span class="badge">${t('sandbox.badgeOff')}</span>`
|
||||
if (sandboxStrong()) return `<span class="badge primary">${t('sandbox.badgeStrong')}</span>`
|
||||
if (policy === 'require') return `<span class="badge">${t('sandbox.badgeRequired')}</span>`
|
||||
return `<span class="badge">${t('sandbox.badgeWeak')}</span>`
|
||||
if (policy === 'none') return 'none'
|
||||
if (policy === 'auto') return sandboxStrong() ? 'relaxed' : 'none'
|
||||
return sandboxStrong() ? policy : 'unavailable'
|
||||
}
|
||||
|
||||
function isolationBadge(game) {
|
||||
const level = isolationLevel(game)
|
||||
if (level === 'none') return `<span class="badge">${t('sandbox.badgeNone')}</span>`
|
||||
if (level === 'unavailable') return `<span class="badge">${t('sandbox.badgeRequired')}</span>`
|
||||
if (level === 'isolated') return `<span class="badge primary">${t('sandbox.badgeIsolated')}</span>`
|
||||
return `<span class="badge primary">${t('sandbox.badgeRelaxed')}</span>`
|
||||
}
|
||||
|
||||
// Portable apps show files/-relative paths; external apps keep absolute ones.
|
||||
|
|
@ -936,6 +963,36 @@ function wireOpenWith() {
|
|||
window.plwl.openwithTake().then(handleOpenWith)
|
||||
}
|
||||
|
||||
// --- isolated hand-off -------------------------------------------------------
|
||||
|
||||
// A document handed to an isolated app is copied in first. Only copies big
|
||||
// enough to be noticed arrive here (main decides, and puts a window up so this
|
||||
// popup can be seen at all).
|
||||
function wireHandoffProgress() {
|
||||
window.plwl.onHandoffProgress(data => {
|
||||
if (!data || !data.notice) return
|
||||
if (data.phase === 'copy') return showHandoffCopy(data)
|
||||
if (data.phase !== 'done' && data.phase !== 'error') return
|
||||
// only ever close our own modal: another one may have opened over it
|
||||
if (qs('#modal').querySelector('#ho-progress')) closeModal()
|
||||
if (data.phase === 'error') toast(t('handoff.failed', data.name, data.error || ''), 'error')
|
||||
})
|
||||
}
|
||||
|
||||
function showHandoffCopy(data) {
|
||||
const modal = qs('#modal').querySelector('#ho-progress')
|
||||
? qs('#modal')
|
||||
: showModal(`
|
||||
<h2>${t('handoff.copying', data.name)}</h2>
|
||||
<div class="progress-row"><progress id="ho-progress"></progress><span class="progress-pct"></span></div>
|
||||
<p class="subtitle" id="ho-status"></p>
|
||||
`)
|
||||
renderProgressBar(modal.querySelector('#ho-progress'), data.bytes, data.totalBytes)
|
||||
const pct = modal.querySelector('.progress-pct')
|
||||
pct.textContent = data.totalBytes ? Math.round(((data.bytes || 0) / data.totalBytes) * 100) + '%' : ''
|
||||
modal.querySelector('#ho-status').textContent = t('handoff.copyNote', formatBytes(data.bytes), formatBytes(data.totalBytes))
|
||||
}
|
||||
|
||||
// Import step 2: register the app in place, nothing is copied.
|
||||
async function doPrefixImport(prefix, srcDir) {
|
||||
const name = srcDir.split('/').filter(Boolean).pop()
|
||||
|
|
@ -1314,14 +1371,24 @@ async function openGameConfig(id) {
|
|||
|
||||
const isolationOptions = () => [
|
||||
{ value: 'auto', label: t('config.isolation.auto') },
|
||||
{ value: 'off', label: t('config.isolation.off') },
|
||||
{ value: 'require', label: t('config.isolation.require') }
|
||||
{ value: 'none', label: t('config.isolation.none') },
|
||||
{ value: 'relaxed', label: t('config.isolation.relaxed') },
|
||||
{ value: 'isolated', label: t('config.isolation.isolated') }
|
||||
]
|
||||
|
||||
// The level names are one word each - what they mean has to be spelled out
|
||||
// right under the select, because the price of relaxed is not visible anywhere
|
||||
// else in the UI.
|
||||
function renderIsolationHint(value) {
|
||||
qs('#gc-isolation-hint').textContent = t(`config.isolationHint.${value || 'auto'}`)
|
||||
}
|
||||
|
||||
function renderIsolationOptions(selected) {
|
||||
const opts = isolationOptions()
|
||||
if (!selects['gc-isolation']) selects['gc-isolation'] = new CustomSelect(qs('#gc-isolation'), opts, selected)
|
||||
else selects['gc-isolation'].update(opts, selected)
|
||||
if (!selects['gc-isolation']) {
|
||||
selects['gc-isolation'] = new CustomSelect(qs('#gc-isolation'), opts, selected, renderIsolationHint)
|
||||
} else selects['gc-isolation'].update(opts, selected)
|
||||
renderIsolationHint(selected)
|
||||
}
|
||||
|
||||
async function createAppShortcut() {
|
||||
|
|
@ -2096,6 +2163,7 @@ async function boot() {
|
|||
wireEvents()
|
||||
wireRealtime()
|
||||
wireOpenWith()
|
||||
wireHandoffProgress()
|
||||
wireLanSync()
|
||||
qs('#loading').remove()
|
||||
qs('#main').style.display = 'flex'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue