mirror of
https://github.com/SteamFramelyHomebrew/framely.git
synced 2026-10-18 06:01:27 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4108d4e5be |
No files matched your search
Generated
+1
-1
@@ -291,7 +291,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "framely"
|
||||
version = "0.4.2-preview.11"
|
||||
version = "0.4.2-preview.12"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "framely"
|
||||
version = "0.4.2-preview.11"
|
||||
version = "0.4.2-preview.12"
|
||||
edition = "2021"
|
||||
license = "AGPL-3.0-only"
|
||||
|
||||
|
||||
+3
-3
@@ -38,7 +38,7 @@ Deleted `/home` data cannot be recovered. OS updates may require manual interven
|
||||
|
||||
Device and installer share the repository with independent versions/tags/workflows:
|
||||
|
||||
- Device: root Cargo version, `v<version>` (currently `v0.4.2-preview.11`), `.github/workflows/release.yml`, Linux ARM64 and pinned CEF.
|
||||
- Device: root Cargo version, `v<version>` (currently `v0.4.2-preview.12`), `.github/workflows/release.yml`, Linux ARM64 and pinned CEF.
|
||||
- Installer: `installer/Cargo.toml`, `installer-v<version>` (currently `installer-v0.4.1-preview.8`), `.github/workflows/installer-release.yml`, Linux x64/ARM64, Windows x64, macOS Intel/Apple Silicon, pinned GPUI Kit.
|
||||
|
||||
Versions need not match or ship together. Update the relevant Cargo.lock with version changes. Installer package/macOS versions use the installer version.
|
||||
@@ -47,8 +47,8 @@ Both workflows cache dependencies/builds by product/platform/toolchain/configura
|
||||
|
||||
```bash
|
||||
# After committing and pushing the intended source:
|
||||
git tag v0.4.2-preview.11
|
||||
git push origin v0.4.2-preview.11
|
||||
git tag v0.4.2-preview.12
|
||||
git push origin v0.4.2-preview.12
|
||||
# Independent installer release:
|
||||
git tag installer-v0.4.1-preview.8
|
||||
git push origin installer-v0.4.1-preview.8
|
||||
|
||||
@@ -48,7 +48,7 @@ CEF 按内容哈希保存在 `/home/.framely/cef/<运行库标识>`,更新时
|
||||
|
||||
Framely 和桌面安装器保留在同一仓库,使用独立的版本号、标签和发布工作流:
|
||||
|
||||
- Framely:版本由根 `Cargo.toml` 定义,标签为 `v<版本>`(当前 `v0.4.2-preview.11`);`.github/workflows/release.yml` 只构建 Frame Linux ARM64 发行,使用锁定的 CEF 并执行自动检查。
|
||||
- Framely:版本由根 `Cargo.toml` 定义,标签为 `v<版本>`(当前 `v0.4.2-preview.12`);`.github/workflows/release.yml` 只构建 Frame Linux ARM64 发行,使用锁定的 CEF 并执行自动检查。
|
||||
- 安装器:版本由 `installer/Cargo.toml` 定义,标签为 `installer-v<版本>`(当前 `installer-v0.4.1-preview.8`);`.github/workflows/installer-release.yml` 只构建 Linux x64/ARM64、Windows x64、macOS x64/ARM64 的原生安装器,使用锁定的 GPUI Kit。
|
||||
|
||||
两个版本不需要相同,也不需要同时发布。修改各自版本时同步对应的 `Cargo.lock`;安装器包名及 macOS 应用版本使用安装器版本。
|
||||
@@ -59,8 +59,8 @@ Framely 和桌面安装器保留在同一仓库,使用独立的版本号、标
|
||||
|
||||
```bash
|
||||
# 提交并推送代码后,选择需要发布的产品
|
||||
git tag v0.4.2-preview.11
|
||||
git push origin v0.4.2-preview.11
|
||||
git tag v0.4.2-preview.12
|
||||
git push origin v0.4.2-preview.12
|
||||
|
||||
# 独立发布安装器,不触发 Framely 构建
|
||||
git tag installer-v0.4.1-preview.8
|
||||
|
||||
@@ -44,10 +44,19 @@ async function choose(label:string){(document.querySelector('.framely-select but
|
||||
if(!document.querySelector('.framely-select button')?.textContent?.includes('正式版'))throw Error('legacy source did not default to stable');
|
||||
button('检查更新').click();await until(()=>!!document.querySelector('.release-card'));
|
||||
if(!document.querySelector('.release-card')?.textContent?.includes('较旧版本'))throw Error('channel downgrade not identified');
|
||||
button('下载并安装').click();await wait();
|
||||
if(downloads!==0||attempts!==0||!document.querySelector('.confirm-box'))throw Error('update bypassed confirmation');
|
||||
button('下载并安装').focus();button('下载并安装').click();await wait();
|
||||
const dialog=document.querySelector('.update-confirmation') as HTMLDialogElement|null;
|
||||
if(downloads!==0||attempts!==0||!dialog?.open||!dialog.matches(':modal'))throw Error('update bypassed modal confirmation');
|
||||
if(!dialog.getAttribute('aria-labelledby')||!dialog.getAttribute('aria-describedby')||document.activeElement!==button('取消'))throw Error('confirmation lacked accessible focus or description');
|
||||
const bounds=dialog.getBoundingClientRect();
|
||||
if(bounds.width<=0||bounds.height<=0||bounds.left<0||bounds.top<0||bounds.right>innerWidth||bounds.bottom>innerHeight)throw Error('confirmation outside viewport');
|
||||
button('检查更新').focus();
|
||||
if(!dialog.contains(document.activeElement))throw Error('background took focus during confirmation');
|
||||
button('取消').click();await wait();
|
||||
if(downloads!==0||attempts!==0)throw Error('cancelled update performed work');
|
||||
if(downloads!==0||attempts!==0||document.querySelector('.update-confirmation')||document.activeElement!==button('下载并安装'))throw Error('cancelled update performed work or did not restore focus');
|
||||
button('下载并安装').click();await wait();
|
||||
document.querySelector('.update-confirmation')!.dispatchEvent(new Event('cancel',{cancelable:true}));await wait();
|
||||
if(document.querySelector('.update-confirmation')||downloads!==0||attempts!==0)throw Error('dialog cancellation performed work');
|
||||
failDownload=true;button('下载并安装').click();await wait();button('确认下载并安装').click();
|
||||
await until(()=>!!document.querySelector('[role=alert]'));
|
||||
if(attempts!==0||!document.querySelector('[role=alert]')?.textContent?.includes('checksum failed'))throw Error('failed verification did not stop installation');
|
||||
@@ -73,7 +82,7 @@ async function choose(label:string){(document.querySelector('.framely-select but
|
||||
if(verification.value!==512*1024||verification.max!==1024*1024||attempts!==2)throw Error('verification byte progress or installation gate incorrect');
|
||||
console.log('FRAMELY_PREVIEW_INSTALL_UPDATE_TESTING');
|
||||
hold=false;await until(()=>installs===1);
|
||||
if(attempts!==3||document.querySelector('.confirm-box'))throw Error('verified download required a second confirmation');
|
||||
if(attempts!==3||document.querySelector('.update-confirmation'))throw Error('verified download required a second confirmation');
|
||||
remount();await wait();
|
||||
failCheck=true;button('检查更新').click();await until(()=>!!document.querySelector('[role=alert]'));
|
||||
if(document.querySelector('.release-card'))throw Error('failed recheck retained stale install');
|
||||
@@ -83,7 +92,7 @@ async function choose(label:string){(document.querySelector('.framely-select but
|
||||
if(!document.querySelector('.framely-select button')?.textContent?.includes('Stable'))throw Error('English channel untranslated');
|
||||
await choose('Testing');button('Check for updates').click();await until(()=>!!document.querySelector('.release-card'));
|
||||
button('Download and install').click();await wait();
|
||||
if(!document.querySelector('.confirm-box')?.textContent?.includes('installed automatically'))throw Error('English update flow untranslated');
|
||||
if(!document.querySelector('.update-confirmation')?.textContent?.includes('installed automatically'))throw Error('English update flow untranslated');
|
||||
hold=true;button('Confirm download and installation').click();
|
||||
await until(()=>!!document.querySelector('progress[aria-label="Verification progress"]'));
|
||||
if(!document.body.textContent?.includes('Download complete. Verifying')||!document.body.textContent?.includes('Verified 0.5 / 1.0 MiB'))throw Error('English verification progress untranslated');
|
||||
|
||||
@@ -9,6 +9,7 @@ input:not([type=checkbox]):not([type=file]),textarea,select{width:100%;padding:1
|
||||
.settings-card{border-top:1px solid var(--line);padding:26px 0;margin:0 0 16px}.settings-card h2{color:#e5e7eb;font-size:24px;font-weight:550}.settings-card>p,.source-form,.settings-card>label{max-width:860px}.settings-card .sub{line-height:1.6}.row{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:16px}.row h2{flex:1}.source-row{display:flex;align-items:flex-start;justify-content:space-between;gap:24px;padding:24px 0;border-bottom:1px solid var(--line)}.source-row>div:first-child{flex:1;min-width:0}.source-row .row{margin-top:0;max-width:470px}.source-row b{margin-right:14px;font-weight:550}.source-row p{margin:8px 0}.source-row small{font-size:16px}.source-url{display:block;margin:8px 0;color:var(--muted);font-size:17px;overflow-wrap:anywhere}.source-form{padding-top:28px}.confirm-box,.release-card{padding:22px;border:1px solid #565d68;background:#202328;margin-top:24px;border-radius:4px}.confirm-box p{color:#c9cfd9}.settings-card details{font-size:18px;margin-top:10px}.settings-card summary{cursor:pointer;padding:8px 0}.plugin{padding:18px 0;border-bottom:1px solid var(--line)}.plugin>small{display:block;overflow-wrap:anywhere}
|
||||
.empty-state{min-height:320px;display:flex;flex-direction:column;align-items:center;justify-content:center;padding:38px;text-align:center;gap:16px}.empty-state h2{font-size:26px;margin:0}.empty-state p{color:var(--muted);font-size:19px;max-width:520px;margin:0}.empty-symbol{color:#808994}.empty{padding:36px;text-align:center;color:var(--muted)}.page-title{padding:16px 24px;display:flex;align-items:center;gap:12px;flex:none;border-bottom:1px solid var(--line)}.page-title strong{flex:1;font-size:24px}.quick:has(.page-title) .shell-header{display:none}iframe{width:100%;flex:1;min-height:0;border:0;background:var(--bg)}.error,.banner{padding:14px 16px;margin:12px 0;border-radius:3px;line-height:1.5;overflow-wrap:anywhere;font-size:18px;flex:none;border-left:3px solid #929ca9;background:#252930;color:#d5dbe4}.error{border-color:#db9797;background:#3a292d;color:#f1b8b8}.app>.error,.app>.banner{margin:12px 26px}.error button{float:right;min-height:30px;padding:0 8px;background:transparent;border:0}.banner p{margin:8px 0}
|
||||
.modal{position:absolute;top:50%;bottom:auto;transform:translateY(-50%);max-height:calc(100% - 56px);left:max(24px,calc((100% - 780px)/2));right:max(24px,calc((100% - 780px)/2));padding:30px;background:#202328;border:1px solid #626974;border-radius:6px;overflow:auto;box-shadow:0 0 0 2000px #000a;z-index:20}.modal h2{font-size:28px;margin-bottom:16px}.modal h3{margin-top:24px}.modal button{margin:8px 8px 8px 0}.modal .source-row{display:block}.modal .row{margin:12px 0}.modal code{display:block;font-size:17px;overflow-wrap:anywhere;background:#141619;padding:16px;border-radius:3px}.modal pre{font:17px/1.6 monospace;white-space:pre-wrap;overflow-wrap:anywhere}.modal-close{float:right}.transfer{z-index:30;display:flex;flex-direction:column;align-items:flex-start;justify-content:center;gap:16px}.transfer>p{margin:0}progress{width:100%;height:10px;accent-color:var(--blue)}.download-progress{margin:24px 0}.system-updates>h2{display:none}.system-updates .error small{display:block;margin-top:8px;color:inherit}.manage-dialog>button{min-width:150px}.manage-dialog>.danger{display:block}
|
||||
.update-confirmation{position:fixed;margin:0;width:auto;color:var(--text);box-shadow:none}.update-confirmation::backdrop{background:#000a}
|
||||
html.notification-view,html.notification-view body{background:transparent}.toasts{height:100%;overflow:auto;padding:8px;background:transparent;font-size:18px}.toast{padding:16px;background:#202328;border:1px solid #454b55;border-radius:8px;margin-bottom:8px}.toast-heading{display:flex;align-items:center;gap:10px}.toast-heading>div{flex:1;min-width:0}.toast-heading small{font-size:14px}.toast h2{font-size:21px;line-height:1.3;margin:2px 0 0;overflow-wrap:anywhere}.toast-close{min-width:36px;min-height:36px;padding:6px;border:0;background:transparent;color:var(--muted)}.toast-content{display:flex;align-items:flex-start;gap:12px;margin-top:12px}.toast-content img{width:80px;height:64px;object-fit:contain;flex:none}.toast-content p{margin:0;line-height:1.45;max-height:104px;overflow:auto;overflow-wrap:anywhere;flex:1}.toast-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:12px}.toast-actions button{min-width:44px;min-height:40px;padding:6px 10px;font-size:20px;border-radius:4px}
|
||||
@media(max-width:1000px){.manager .workspace{grid-template-columns:186px minmax(0,1fr)}.manager nav{padding:20px 8px}.manager nav button{padding:12px 10px;font-size:18px}.content{padding:24px}.row-state{width:90px}.row-version{width:85px}.list-columns{grid-template-columns:minmax(0,1fr) 90px 85px 184px}.list-heading{flex-wrap:wrap}.manager .plugin-copy small{white-space:normal}.source-row{flex-direction:column}.store-version{display:none}}
|
||||
@media(max-width:700px){.manager .workspace{display:flex}.manager nav{flex-direction:row;padding:8px;overflow:auto;border-right:0;border-bottom:1px solid var(--line)}.manager nav button{white-space:nowrap}.nav-foot{display:none}.shell-header{padding:14px 18px}.header-label{display:none}.shell-header .primary{font-size:18px}.content{padding:18px}.list-columns,.row-state,.row-version,.manager .plugin-switch{display:none}.manager .plugin-meta{display:flex}.list-heading .search-field{width:100%}.store-card{gap:14px;padding:18px 0}.store-card>.badge{min-width:65px;font-size:15px}.store-card>.plugin-avatar{width:64px!important;height:64px!important}.store-card>svg{display:none}.store-copy p{font-size:17px}.store-copy h2{font-size:22px}.store-filters .search-field{min-width:100%}.detail-heading{flex-wrap:wrap}.detail-heading .primary{width:100%}.modal{left:14px;right:14px;max-height:calc(100% - 28px);padding:22px}}
|
||||
|
||||
+14
-5
@@ -1,5 +1,5 @@
|
||||
import {t} from './i18n';
|
||||
import React,{useEffect,useState} from 'react';
|
||||
import React,{useEffect,useId,useLayoutEffect,useRef,useState} from 'react';
|
||||
import {api} from './api';
|
||||
import {runJob,Job,compareVersions} from './store';
|
||||
import {Select} from './localized-select';
|
||||
@@ -9,6 +9,13 @@ export function SystemUpdates({status,refresh}:{status:any;refresh:()=>Promise<v
|
||||
const source:UpdateSource|null=status.database.updateSource??null;
|
||||
const channel:UpdateChannel=status.database.updateChannel??'stable';
|
||||
const[error,setError]=useState(''),[busy,setBusy]=useState(false),[job,setJob]=useState<Job|null>(null),[release,setRelease]=useState<any>(null),[ready,setReady]=useState(false),[checked,setChecked]=useState(false),[confirm,setConfirm]=useState<'apply'|'rollback'|null>(null),[installing,setInstalling]=useState(false);
|
||||
const confirmation=useRef<HTMLDialogElement>(null),cancelConfirmation=useRef<HTMLButtonElement>(null),confirmationTitle=useId(),confirmationDescription=useId();
|
||||
useLayoutEffect(()=>{
|
||||
const dialog=confirmation.current;if(!confirm||!dialog)return;
|
||||
const previous=document.activeElement as HTMLElement|null;
|
||||
dialog.showModal();cancelConfirmation.current?.focus();
|
||||
return()=>{dialog.close();if(previous?.isConnected)previous.focus();};
|
||||
},[confirm]);
|
||||
function reset(){setRelease(null);setReady(false);setChecked(false);setConfirm(null);}
|
||||
useEffect(reset,[source?.url,channel]);
|
||||
const progress=job?.kind==='system.download'?job:status.systemUpdate;
|
||||
@@ -71,15 +78,17 @@ export function SystemUpdates({status,refresh}:{status:any;refresh:()=>Promise<v
|
||||
</div>}
|
||||
{error&&<p className="error" role="alert">{error}</p>}
|
||||
{installing&&<p className="banner">{t('正在切换版本。Framely 入口会在服务恢复后重新出现。')}</p>}
|
||||
{confirm&&<div className="confirm-box">
|
||||
<h3>{confirm==='apply'?t('安装 Framely {0}?',{'0':release.version}):t('回滚 Framely?')}</h3>
|
||||
{confirm&&<dialog ref={confirmation} className="modal update-confirmation" aria-labelledby={confirmationTitle} aria-describedby={confirmationDescription} onCancel={e=>{e.preventDefault();setConfirm(null);}} onKeyDown={e=>{if(e.key==='Escape'){e.stopPropagation();setConfirm(null);}}}>
|
||||
<h2 id={confirmationTitle}>{confirm==='apply'?t('安装 Framely {0}?',{'0':release.version}):t('回滚 Framely?')}</h2>
|
||||
<div id={confirmationDescription}>
|
||||
{confirm==='apply'&&!ready&&<p>{t('确认后将下载并校验发行包,校验通过后自动安装。')}</p>}
|
||||
<p>{t('插件和数据会保留,Framely 界面会暂时关闭。SteamVR 不会被重启。')}</p>
|
||||
{confirm==='apply'&&older&&<p className="banner">{t('所选版本早于当前版本,安装将切换到较旧版本。')}</p>}
|
||||
</div>
|
||||
<div className="row">
|
||||
<button className="primary" disabled={installing||busy} onClick={()=>void confirmUpdate()}>{confirm==='apply'?(ready?t('确认安装'):t('确认下载并安装')):t('确认回滚')}</button>
|
||||
<button disabled={installing} onClick={()=>setConfirm(null)}>{t('取消')}</button>
|
||||
<button ref={cancelConfirmation} disabled={installing} onClick={()=>setConfirm(null)}>{t('取消')}</button>
|
||||
</div>
|
||||
</div>}
|
||||
</dialog>}
|
||||
</section>;
|
||||
}
|
||||
Reference in new issue
Block a user