Compare commits

...
14 Commits
Author SHA1 Message Date
kfj001 c3c8f24857 Merge pull request 'align runner label to server config' (#12) from align-runner-label into master
CI / test (push) Successful in 5m51s
Release / release (push) Successful in 37s
Reviewed-on: http://192.168.1.74:30008/WesternTechnologies/goon_plugin/pulls/12
2026-10-07 16:08:20 -07:00
kfj001 b071dbda87 Update .github/workflows/ci.yml
CI / test (pull_request) Successful in 5m41s
2026-10-07 14:41:13 -07:00
kfj001 1924834cd5 Update .github/workflows/ci.yml
CI / test (pull_request) Canceled after 0s
2026-10-07 14:16:35 -07:00
kfj001 1a3616f026 Update .github/workflows/ci.yml
CI / test (pull_request) Successful in 5m49s
sets specific ubuntu version for CI tests
2026-10-07 14:14:27 -07:00
kfj001 6ed6e4277a Update .github/workflows/release.yml
CI / test (pull_request) Successful in 10s
sets specific version to perform releases on
2026-10-07 14:11:50 -07:00
robot 59456918bc align runner label to server config: ubuntu_latest
CI / test (pull_request) Successful in 5m35s
2026-10-07 21:03:34 +00:00
kfj001 62ce7ea788 Merge pull request 'chore: bump version to 0.2.0' (#11) from chore/bump-version-0.2.0 into master
CI / test (push) Successful in 5m45s
Release / release (push) Successful in 23s
Reviewed-on: http://192.168.1.74:30008/kfj001/goon_plugin/pulls/11
Reviewed-by: kfj001 <kfj001@gmail.com>
2026-10-07 13:23:39 -07:00
Hermes 352b14f7d8 chore: bump version to 0.2.0
CI / test (pull_request) Successful in 6m24s
2026-10-06 06:35:19 +00:00
kfj001 f46285fe4e Merge pull request 'feat(viewer): persist hide/show instructions state in chrome.storage.local' (#10) from feature/durable-instructions-visibility into master
CI / test (push) Successful in 6m18s
Reviewed-on: http://192.168.1.74:30008/kfj001/goon_plugin/pulls/10
Reviewed-by: kfj001 <kfj001@gmail.com>
2026-09-30 18:54:19 -07:00
robot 42c1ad75c8 feat(viewer): persist hide/show instructions state in chrome.storage.local
CI / test (pull_request) Successful in 6m29s
Read the instructions panel's visibility on mount from
chrome.storage.local['goon_instructions_visible'] (absent => shown, the
first-time default) and persist the new value on every user toggle, so a
'hide instructions' choice survives popup reloads and service-worker
termination and is respected across the app via the shared storage key.

Adds unit tests: stored-hidden honored on mount, persistence on both toggle
directions, and first-time (absent key) defaulting to shown.
2026-09-30 03:09:25 +00:00
kfj001 fbc836d9c4 Merge pull request 'ci: make release step idempotent + fix Gitea asset upload' (#9) from feature/gitea-release-workflow into master
CI / test (push) Successful in 5m43s
Release / release (push) Successful in 13s
Reviewed-on: http://192.168.1.74:30008/kfj001/goon_plugin/pulls/9
Reviewed-by: robot <robot@westerntechnologies.duckdns.org>
2026-08-31 10:27:12 -07:00
kfj001 de9d537a75 ci: make release step idempotent + fix Gitea asset upload
CI / test (pull_request) Successful in 5m37s
- resolve-or-create the release by tag to avoid the 409 'Release is has no Tag'
  when a release already exists for the tag (e.g. one made manually in the UI)
- use POST /releases/{id}/assets with multipart field 'attachment'
  (was the obsolete /_attachments route with field 'file')
2026-08-31 00:33:53 -07:00
kfj001 23a3f00df9 Merge pull request 'ci: add tag-based Gitea release workflow + bump to 0.1.1' (#8) from feature/gitea-release-workflow into master
CI / test (push) Successful in 5m32s
Release / release (push) Failing after 13s
Reviewed-on: http://192.168.1.74:30008/kfj001/goon_plugin/pulls/8
2026-08-30 23:57:59 -07:00
kfj001 0a3225035c ci: add tag-based Gitea release workflow + bump to 0.1.1
CI / test (pull_request) Successful in 5m48s
2026-08-30 23:31:12 -07:00
6 changed files with 172 additions and 8 deletions

No files matched your search

+1 -1
View File
@@ -8,7 +8,7 @@ on:
jobs: jobs:
test: test:
runs-on: ubuntu-latest runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
+81
View File
@@ -0,0 +1,81 @@
name: Release
on:
push:
tags:
- 'v*'
jobs:
release:
runs-on: ubuntu-24.04
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Package dist.zip
shell: bash
run: |
cd dist
zip -r "../vidplex-${{ github.ref_name }}.zip" .
- name: Create Gitea release
shell: bash
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
GITEA_API: ${{ secrets.GITEA_API || 'http://192.168.1.74:30008/api/v1' }}
REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }}
run: |
if [ -z "$GITEA_TOKEN" ]; then
echo "::warning::GITEA_TOKEN secret is not set; skipping release creation. Add it in Gitea repo Settings > Secrets (repo scope)."
exit 0
fi
json_id() { node -e 'let d="";process.stdin.on("data",c=>d+=c).on("end",()=>{const j=JSON.parse(d||"{}");if(j.id==null){console.error("no release id:",JSON.stringify(j));process.exit(1)}console.log(j.id)})'; }
release_id=$(
curl -s -o /tmp/rel.json -w "%{http_code}" \
-H "Authorization: token $GITEA_TOKEN" \
"$GITEA_API/repos/$REPO/releases/tags/$TAG"
)
if [ "$release_id" = "200" ]; then
echo "Release for $TAG already exists; reusing it."
release_id=$(json_id < /tmp/rel.json)
else
echo "Creating release for $TAG."
release_id=$(
curl -s -o /tmp/rel.json -w "%{http_code}" -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"tag_name\": \"$TAG\", \"name\": \"$TAG\", \"body\": \"Automated release build for $TAG\"}" \
"$GITEA_API/repos/$REPO/releases"
)
case "$release_id" in
201|200) release_id=$(json_id < /tmp/rel.json) ;;
*) echo "::error::release create returned HTTP $release_id: $(cat /tmp/rel.json)"; exit 1 ;;
esac
fi
echo "Uploading asset to release $release_id."
resp=$(curl -s -o /tmp/att.json -w "%{http_code}" -X POST \
-H "Authorization: token $GITEA_TOKEN" \
-F "attachment=@vidplex-${TAG}.zip" \
"$GITEA_API/repos/$REPO/releases/$release_id/assets")
if [ "$resp" != "200" ] && [ "$resp" != "201" ]; then
echo "::error::asset upload returned HTTP $resp: $(cat /tmp/att.json)"
exit 1
fi
cat /tmp/att.json
echo
+69 -4
View File
@@ -130,16 +130,26 @@ function installPopupDom() {
}; };
} }
async function mountPopup() { async function mountPopupWithStorage(storageGet = async () => ({}), captureWrite = null) {
const { dom, cleanup } = installPopupDom(); const { dom, cleanup } = installPopupDom();
// Override the chrome.storage.local stub so we can seed a value and capture writes.
const writes = [];
globalThis.chrome.storage.local.get = async (...args) => {
const res = await storageGet(...args);
return res;
};
globalThis.chrome.storage.local.set = async (items) => {
if (captureWrite) captureWrite(items); else writes.push(items);
};
const moduleUrl = new URL('../src/viewer/index.js?test=' + Date.now(), import.meta.url); const moduleUrl = new URL('../src/viewer/index.js?test=' + Date.now(), import.meta.url);
await import(moduleUrl.href); await import(moduleUrl.href);
return { dom, cleanup }; const flush = () => new Promise((resolve) => setTimeout(resolve, 0));
return { dom, cleanup, writes, flush };
} }
describe('Viewer popup instructions', () => { describe('Viewer popup instructions', () => {
it('renders the drafted instructions copy on mount with accessible labeling', async () => { it('renders the drafted instructions copy on mount with accessible labeling', async () => {
const { dom, cleanup } = await mountPopup(); const { dom, cleanup } = await mountPopupWithStorage();
try { try {
const doc = dom.window.document; const doc = dom.window.document;
const region = doc.querySelector('.instructions-panel'); const region = doc.querySelector('.instructions-panel');
@@ -180,7 +190,7 @@ describe('Viewer popup instructions', () => {
}); });
it('toggles the instructions panel open and closed', async () => { it('toggles the instructions panel open and closed', async () => {
const { dom, cleanup } = await mountPopup(); const { dom, cleanup } = await mountPopupWithStorage();
try { try {
const doc = dom.window.document; const doc = dom.window.document;
const toggle = doc.getElementById('instructions-toggle'); const toggle = doc.getElementById('instructions-toggle');
@@ -201,4 +211,59 @@ describe('Viewer popup instructions', () => {
cleanup(); cleanup();
} }
}); });
it('honors a stored hidden state on mount', async () => {
const { dom, cleanup } = await mountPopupWithStorage(
async () => ({ goon_instructions_visible: false })
);
try {
const doc = dom.window.document;
const toggle = doc.getElementById('instructions-toggle');
const body = doc.getElementById('instructions-body');
// Wait for the async storage.get() to resolve and apply.
await new Promise((r) => setTimeout(r, 0));
if (toggle.getAttribute('aria-expanded') !== 'false') throw new Error('Stored hidden state should start collapsed');
if (!body.hidden) throw new Error('Stored hidden state should start hidden');
if (toggle.textContent !== 'Show instructions') throw new Error('Stored hidden state should show the "Show instructions" label');
} finally {
cleanup();
}
});
it('persists the new visibility on every user toggle', async () => {
let written = null;
const { dom, cleanup, flush } = await mountPopupWithStorage(
async () => ({}),
(items) => { written = items; }
);
try {
const doc = dom.window.document;
const toggle = doc.getElementById('instructions-toggle');
// Collapse -> must persist false.
toggle.click();
await flush();
if (written === null) throw new Error('Collapsing the panel must write to chrome.storage.local');
if (written.goon_instructions_visible !== false) throw new Error('Collapsing must persist goon_instructions_visible=false');
// Expand -> must persist true.
toggle.click();
await flush();
if (written.goon_instructions_visible !== true) throw new Error('Expanding must persist goon_instructions_visible=true');
} finally {
cleanup();
}
});
it('defaults a first-time user (no stored key) to shown', async () => {
const { dom, cleanup } = await mountPopupWithStorage(async () => ({}));
try {
const doc = dom.window.document;
const toggle = doc.getElementById('instructions-toggle');
const body = doc.getElementById('instructions-body');
await new Promise((r) => setTimeout(r, 0));
if (toggle.getAttribute('aria-expanded') !== 'true') throw new Error('First-time user should start expanded');
if (body.hidden) throw new Error('First-time instructions body should be visible on mount');
} finally {
cleanup();
}
});
}); });
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "VidPlex", "name": "VidPlex",
"version": "0.1.0", "version": "0.2.0",
"description": "Find videos on any page and view them in a grid.", "description": "Find videos on any page and view them in a grid.",
"permissions": [ "permissions": [
"storage", "storage",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "vidplex", "name": "vidplex",
"version": "0.1.0", "version": "0.2.0",
"private": true, "private": true,
"description": "Chrome extension: find videos on a page and view them in a grid.", "description": "Chrome extension: find videos on a page and view them in a grid.",
"type": "module", "type": "module",
+19 -1
View File
@@ -6,6 +6,11 @@
const STORAGE_KEY = 'goon_videos'; const STORAGE_KEY = 'goon_videos';
const TAGGED_KEY = 'goon_tagged'; const TAGGED_KEY = 'goon_tagged';
// Durable "hide instructions" toggle. Absent key === SHOWN (true), matching the
// first-time "expanded on mount" behavior. chrome.storage.local is extension-wide,
// so the viewer window and every popup instance read/write the same value — the
// preference is respected application-wide and survives reloads / SW termination.
const INSTRUCTIONS_VISIBLE_KEY = 'goon_instructions_visible';
// Set to true to enable verbose console output in the popup. // Set to true to enable verbose console output in the popup.
const DEBUG = true; // TEMP: enabled for debugging — revert to false when done const DEBUG = true; // TEMP: enabled for debugging — revert to false when done
@@ -48,9 +53,22 @@ function setInstructionsExpanded(expanded) {
} }
if (instructionsToggle && instructionsBody) { if (instructionsToggle && instructionsBody) {
// On mount, read the stored visibility. An absent key means SHOWN (true), so a
// first-time user still sees the steps. The viewer window reads the same
// extension-wide key, so the choice is respected across the whole app.
chrome.storage.local.get(INSTRUCTIONS_VISIBLE_KEY).then(stored => {
const visible = stored[INSTRUCTIONS_VISIBLE_KEY];
setInstructionsExpanded(visible === undefined ? true : visible);
}).catch(() => {
// Any read failure falls back to shown.
setInstructionsExpanded(true); setInstructionsExpanded(true);
});
// On every user toggle, apply in-DOM and persist. Both directions write, so
// the state survives reloads and SW termination until the user flips it again.
instructionsToggle.addEventListener('click', () => { instructionsToggle.addEventListener('click', () => {
setInstructionsExpanded(instructionsBody.hidden); const next = instructionsBody.hidden;
setInstructionsExpanded(next);
chrome.storage.local.set({ [INSTRUCTIONS_VISIBLE_KEY]: next });
}); });
} }