Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
+5
-1
@@ -10,6 +10,10 @@
|
||||
"Every 5 min": "Alle 5 Minuten",
|
||||
"Every 10 min": "Alle 10 Minuten",
|
||||
"Every 30 min": "Alle 30 Minuten",
|
||||
"Every Hour": "Jede Stunde"
|
||||
"Every Hour": "Jede Stunde",
|
||||
"Gitea URL": "Gitea URL",
|
||||
"Owner": "Eigentümer",
|
||||
"Repository": "Repository",
|
||||
"API Token": "API Token"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
"Every 5 min": "Every 5 min",
|
||||
"Every 10 min": "Every 10 min",
|
||||
"Every 30 min": "Every 30 min",
|
||||
"Every Hour": "Every Hour"
|
||||
"Every Hour": "Every Hour",
|
||||
"Gitea URL": "Gitea URL",
|
||||
"Owner": "Owner",
|
||||
"Repository": "Repository",
|
||||
"API Token": "API Token"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,19 @@
|
||||
],
|
||||
"Tooltip": "Displays GitHub Copilot usage percentage as a gauge",
|
||||
"UUID": "dev.mars3142.ulanzideck.collection.copilot"
|
||||
},
|
||||
{
|
||||
"Name": "Gitea Actions",
|
||||
"Icon": "assets/icons/gitea.png",
|
||||
"PropertyInspectorPath": "property-inspector/gitea/inspector.html",
|
||||
"States": [
|
||||
{
|
||||
"Name": "Default",
|
||||
"Image": "assets/icons/gitea.png"
|
||||
}
|
||||
],
|
||||
"Tooltip": "Shows the latest Gitea Actions run status for a repository",
|
||||
"UUID": "dev.mars3142.ulanzideck.collection.gitea"
|
||||
}
|
||||
],
|
||||
"OS": [
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
class GiteaAction {
|
||||
constructor($UD, context) {
|
||||
this.$UD = $UD;
|
||||
this.context = context;
|
||||
this.config = {
|
||||
url: '',
|
||||
owner: '',
|
||||
repo: '',
|
||||
token: '',
|
||||
refreshRate: '4',
|
||||
};
|
||||
this.refreshTimer = null;
|
||||
this.debounceTimer = 0;
|
||||
}
|
||||
|
||||
setActive() {}
|
||||
|
||||
onClear() {
|
||||
if (this.refreshTimer) {
|
||||
clearInterval(this.refreshTimer);
|
||||
this.refreshTimer = null;
|
||||
}
|
||||
if (this.debounceTimer) {
|
||||
clearTimeout(this.debounceTimer);
|
||||
this.debounceTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
setParams(jsn) {
|
||||
this.config = Object.assign(this.config, (jsn && jsn.param) || {});
|
||||
this.start();
|
||||
}
|
||||
|
||||
onRun() {
|
||||
if (this.config.url && this.config.owner && this.config.repo) {
|
||||
this.$UD.openUrl(`${this.config.url}/${this.config.owner}/${this.config.repo}/actions`);
|
||||
}
|
||||
}
|
||||
|
||||
start() {
|
||||
this.onClear();
|
||||
this.fetchRun();
|
||||
const duration = this.getRefreshDuration();
|
||||
if (duration > 0) {
|
||||
this.refreshTimer = setInterval(() => this.fetchRun(), duration * 60 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
async fetchRun() {
|
||||
if (this.debounceTimer) clearTimeout(this.debounceTimer);
|
||||
this.debounceTimer = setTimeout(async () => {
|
||||
if (!this.config.url || !this.config.owner || !this.config.repo) {
|
||||
this.renderButton(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const headers = this.config.token ? { Authorization: `token ${this.config.token}` } : {};
|
||||
const response = await fetch(
|
||||
`${this.config.url}/api/v1/repos/${this.config.owner}/${this.config.repo}/actions/runs?limit=1`,
|
||||
{ headers }
|
||||
);
|
||||
if (!response.ok) {
|
||||
this.renderButton({ error: response.status });
|
||||
return;
|
||||
}
|
||||
const data = await response.json();
|
||||
const run = data.workflow_runs && data.workflow_runs[0];
|
||||
this.renderButton(run || null);
|
||||
} catch (e) {
|
||||
this.renderButton({ error: 'ERR' });
|
||||
}
|
||||
}, 300);
|
||||
}
|
||||
|
||||
renderButton(run) {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = 196;
|
||||
canvas.height = 196;
|
||||
const ctx = canvas.getContext('2d');
|
||||
|
||||
ctx.fillStyle = '#000000';
|
||||
ctx.fillRect(0, 0, 196, 196);
|
||||
|
||||
ctx.textBaseline = 'middle';
|
||||
ctx.textAlign = 'center';
|
||||
|
||||
// Owner — top
|
||||
const ownerLabel = this.config.owner || '?';
|
||||
ctx.fillStyle = '#888888';
|
||||
ctx.font = '18px "Source Han Sans SC"';
|
||||
ctx.fillText(ownerLabel.length > 14 ? ownerLabel.slice(0, 13) + '…' : ownerLabel, 98, 22);
|
||||
|
||||
// Repo name — below owner
|
||||
const repoLabel = this.config.repo || '?';
|
||||
ctx.fillStyle = '#7ec8e3';
|
||||
ctx.font = 'bold 22px "Source Han Sans SC"';
|
||||
ctx.fillText(repoLabel.length > 12 ? repoLabel.slice(0, 11) + '…' : repoLabel, 98, 46);
|
||||
|
||||
// Status symbol — center
|
||||
let symbol, color;
|
||||
if (!run) {
|
||||
symbol = '?';
|
||||
color = '#666666';
|
||||
} else if (run.error) {
|
||||
symbol = '✗';
|
||||
color = '#ff6b6b';
|
||||
} else if (run.status === 'running' || run.status === 'waiting') {
|
||||
symbol = '⟳';
|
||||
color = '#f0c040';
|
||||
} else if (run.conclusion === 'success') {
|
||||
symbol = '✓';
|
||||
color = '#6bff6b';
|
||||
} else if (run.conclusion === 'failure') {
|
||||
symbol = '✗';
|
||||
color = '#ff6b6b';
|
||||
} else if (run.conclusion === 'cancelled') {
|
||||
symbol = '⊘';
|
||||
color = '#aaaaaa';
|
||||
} else {
|
||||
symbol = '?';
|
||||
color = '#666666';
|
||||
}
|
||||
|
||||
ctx.fillStyle = color;
|
||||
ctx.font = 'bold 76px "Source Han Sans SC"';
|
||||
ctx.fillText(symbol, 98, 115);
|
||||
|
||||
// Run info — bottom, single line
|
||||
if (run && !run.error) {
|
||||
const branch = run.head_branch || '';
|
||||
const num = `#${run.run_number}`;
|
||||
const info = branch ? `${num} · ${branch}` : num;
|
||||
ctx.fillStyle = '#aaaaaa';
|
||||
ctx.font = 'bold 20px "Source Han Sans SC"';
|
||||
ctx.fillText(info.length > 16 ? info.slice(0, 15) + '…' : info, 98, 172);
|
||||
}
|
||||
|
||||
this.$UD.setBaseDataIcon(this.context, canvas.toDataURL('image/png'));
|
||||
}
|
||||
|
||||
getRefreshDuration() {
|
||||
switch (this.config.refreshRate) {
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 5;
|
||||
case '4': return 10;
|
||||
case '5': return 30;
|
||||
case '6': return 60;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
<script src="./actions/PetrolAction.js"></script>
|
||||
<script src="./actions/CopilotAction.js"></script>
|
||||
<script src="./actions/GiteaAction.js"></script>
|
||||
<script src="./app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -15,6 +15,8 @@ $UD.onAdd(jsn => {
|
||||
ACTION_CACHES[context] = new PetrolAction($UD, context);
|
||||
} else if (name === 'copilot') {
|
||||
ACTION_CACHES[context] = new CopilotAction($UD, context);
|
||||
} else if (name === 'gitea') {
|
||||
ACTION_CACHES[context] = new GiteaAction($UD, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
|
||||
<title>Gitea Actions</title>
|
||||
<link rel="stylesheet" href="../../libs/css/uspi.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="uspi-wrapper">
|
||||
<form id="property-inspector">
|
||||
<div class="uspi-item">
|
||||
<div class="uspi-item-label" data-localize>Gitea URL</div>
|
||||
<input class="uspi-item-value" type="text" name="url" placeholder="https://git.example.com" />
|
||||
</div>
|
||||
<div class="uspi-item">
|
||||
<div class="uspi-item-label" data-localize>Owner</div>
|
||||
<input class="uspi-item-value" type="text" name="owner" placeholder="owner" />
|
||||
</div>
|
||||
<div class="uspi-item">
|
||||
<div class="uspi-item-label" data-localize>Repository</div>
|
||||
<input class="uspi-item-value" type="text" name="repo" placeholder="repo-name" />
|
||||
</div>
|
||||
<div class="uspi-item">
|
||||
<div class="uspi-item-label" data-localize>API Token</div>
|
||||
<input class="uspi-item-value" type="password" name="token" placeholder="gitea_token..." />
|
||||
</div>
|
||||
<div class="uspi-item">
|
||||
<div class="uspi-item-label" data-localize>Refresh Rate</div>
|
||||
<select class="uspi-item-value" name="refreshRate">
|
||||
<option value="0" data-localize>On Press</option>
|
||||
<option value="1" data-localize>Every 1 min</option>
|
||||
<option value="2" data-localize>Every 2 min</option>
|
||||
<option value="3" data-localize>Every 5 min</option>
|
||||
<option value="4" selected data-localize>Every 10 min</option>
|
||||
<option value="5" data-localize>Every 30 min</option>
|
||||
<option value="6" data-localize>Every Hour</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="../../libs/js/constants.js"></script>
|
||||
<script src="../../libs/js/eventEmitter.js"></script>
|
||||
<script src="../../libs/js/utils.js"></script>
|
||||
<script src="../../libs/js/ulanzideckApi.js"></script>
|
||||
<script src="./inspector.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,31 @@
|
||||
let ACTION_SETTING = {};
|
||||
let form = '';
|
||||
|
||||
$UD.connect('dev.mars3142.ulanzideck.collection.gitea');
|
||||
|
||||
$UD.onConnected(() => {
|
||||
form = document.querySelector('#property-inspector');
|
||||
|
||||
form.addEventListener('input', Utils.debounce(() => {
|
||||
const value = Utils.getFormValue(form);
|
||||
ACTION_SETTING = { ...ACTION_SETTING, ...value };
|
||||
$UD.sendParamFromPlugin(ACTION_SETTING);
|
||||
}));
|
||||
});
|
||||
|
||||
$UD.onAdd(jsn => {
|
||||
if (jsn && jsn.param) settingSaveParam(jsn.param);
|
||||
});
|
||||
|
||||
$UD.onParamFromApp(jsn => {
|
||||
settingSaveParam((jsn && jsn.param) || {});
|
||||
});
|
||||
|
||||
$UD.onParamFromPlugin(jsn => {
|
||||
settingSaveParam((jsn && jsn.param) || {});
|
||||
});
|
||||
|
||||
function settingSaveParam(params) {
|
||||
ACTION_SETTING = { ...ACTION_SETTING, ...params };
|
||||
if (form) Utils.setFormValue(ACTION_SETTING, form);
|
||||
}
|
||||
Reference in New Issue
Block a user