arrayview 0.12.0__tar.gz → 0.12.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. {arrayview-0.12.0 → arrayview-0.12.1}/AGENTS.md +6 -0
  2. arrayview-0.12.1/CONTRIBUTING.md +93 -0
  3. {arrayview-0.12.0 → arrayview-0.12.1}/PKG-INFO +1 -1
  4. {arrayview-0.12.0 → arrayview-0.12.1}/pyproject.toml +1 -1
  5. arrayview-0.12.1/scripts/release.sh +138 -0
  6. {arrayview-0.12.0 → arrayview-0.12.1}/uv.lock +1 -1
  7. arrayview-0.12.0/.tmp-vsix/extension/extension.js +0 -343
  8. arrayview-0.12.0/.tmp-vsix/extension/package.json +0 -13
  9. arrayview-0.12.0/scripts/release.sh +0 -78
  10. {arrayview-0.12.0 → arrayview-0.12.1}/.claude/skills/invocation-consistency/SKILL.md +0 -0
  11. {arrayview-0.12.0 → arrayview-0.12.1}/.claude/skills/modes-consistency/SKILL.md +0 -0
  12. {arrayview-0.12.0 → arrayview-0.12.1}/.claude/skills/ui-consistency-audit/SKILL.md +0 -0
  13. {arrayview-0.12.0 → arrayview-0.12.1}/.claude/skills/viewer-ui-checklist/SKILL.md +0 -0
  14. {arrayview-0.12.0 → arrayview-0.12.1}/.claude/skills/visual-bug-fixing/SKILL.md +0 -0
  15. {arrayview-0.12.0 → arrayview-0.12.1}/.github/workflows/docs.yml +0 -0
  16. {arrayview-0.12.0 → arrayview-0.12.1}/.github/workflows/python-publish.yml +0 -0
  17. {arrayview-0.12.0 → arrayview-0.12.1}/.gitignore +0 -0
  18. {arrayview-0.12.0 → arrayview-0.12.1}/.python-version +0 -0
  19. {arrayview-0.12.0 → arrayview-0.12.1}/LICENSE +0 -0
  20. {arrayview-0.12.0 → arrayview-0.12.1}/README.md +0 -0
  21. {arrayview-0.12.0 → arrayview-0.12.1}/docs/comparing.md +0 -0
  22. {arrayview-0.12.0 → arrayview-0.12.1}/docs/configuration.md +0 -0
  23. {arrayview-0.12.0 → arrayview-0.12.1}/docs/display.md +0 -0
  24. {arrayview-0.12.0 → arrayview-0.12.1}/docs/index.md +0 -0
  25. {arrayview-0.12.0 → arrayview-0.12.1}/docs/loading.md +0 -0
  26. {arrayview-0.12.0 → arrayview-0.12.1}/docs/logo.png +0 -0
  27. {arrayview-0.12.0 → arrayview-0.12.1}/docs/measurement.md +0 -0
  28. {arrayview-0.12.0 → arrayview-0.12.1}/docs/remote.md +0 -0
  29. {arrayview-0.12.0 → arrayview-0.12.1}/docs/stylesheets/extra.css +0 -0
  30. {arrayview-0.12.0 → arrayview-0.12.1}/docs/viewing.md +0 -0
  31. {arrayview-0.12.0 → arrayview-0.12.1}/matlab/arrayview.m +0 -0
  32. {arrayview-0.12.0 → arrayview-0.12.1}/mkdocs.yml +0 -0
  33. {arrayview-0.12.0 → arrayview-0.12.1}/plans/webview/LOG.md +0 -0
  34. {arrayview-0.12.0 → arrayview-0.12.1}/scripts/demo.py +0 -0
  35. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/ARCHITECTURE.md +0 -0
  36. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/__init__.py +0 -0
  37. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/__main__.py +0 -0
  38. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_app.py +0 -0
  39. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_config.py +0 -0
  40. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_icon.png +0 -0
  41. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_io.py +0 -0
  42. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_launcher.py +0 -0
  43. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_platform.py +0 -0
  44. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_render.py +0 -0
  45. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_segmentation.py +0 -0
  46. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_server.py +0 -0
  47. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_session.py +0 -0
  48. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_shell.html +0 -0
  49. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_stdio_server.py +0 -0
  50. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_torch.py +0 -0
  51. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_viewer.html +0 -0
  52. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/_vscode.py +0 -0
  53. {arrayview-0.12.0 → arrayview-0.12.1}/src/arrayview/arrayview-opener.vsix +0 -0
  54. {arrayview-0.12.0 → arrayview-0.12.1}/tests/conftest.py +0 -0
  55. {arrayview-0.12.0 → arrayview-0.12.1}/tests/make_vectorfield_test_arrays.py +0 -0
  56. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_api.py +0 -0
  57. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_browser.py +0 -0
  58. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_cli.py +0 -0
  59. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_command_reachability.py +0 -0
  60. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_config.py +0 -0
  61. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_interactions.py +0 -0
  62. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_large_arrays.py +0 -0
  63. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_mode_consistency.py +0 -0
  64. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_mode_matrix.py +0 -0
  65. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_mode_roundtrip.py +0 -0
  66. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_nifti_meta.py +0 -0
  67. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_rgb_pixel_art.py +0 -0
  68. {arrayview-0.12.0 → arrayview-0.12.1}/tests/test_torch.py +0 -0
  69. {arrayview-0.12.0 → arrayview-0.12.1}/tests/ui_audit.py +0 -0
  70. {arrayview-0.12.0 → arrayview-0.12.1}/tests/visual_smoke.py +0 -0
  71. {arrayview-0.12.0 → arrayview-0.12.1}/vscode-extension/LICENSE +0 -0
  72. {arrayview-0.12.0 → arrayview-0.12.1}/vscode-extension/extension.js +0 -0
  73. {arrayview-0.12.0 → arrayview-0.12.1}/vscode-extension/package.json +0 -0
@@ -29,6 +29,12 @@ Load the relevant skill before touching the corresponding area.
29
29
  - All colorbar state (animation, window/level, hover, drag) flows through `primaryCb` ColorBar instance — never read/write legacy globals. Multiview colorbars sync via `primaryCb`.
30
30
  - Keybinds flow through the command registry (`commands` / `keybinds` in `_viewer.html`), not inline keydown branches. The help overlay auto-generates from command `title` fields — do not hand-edit it.
31
31
 
32
+ ## Contributing
33
+
34
+ Before creating a PR or making user-facing changes, read `CONTRIBUTING.md`. It defines the design
35
+ language, keybinding conventions, overlay/popup patterns, and testing requirements. All PRs that
36
+ touch the viewer must follow it.
37
+
32
38
  ## Execution
33
39
 
34
40
  Always use **subagent-driven development** for implementation. Commit completed work automatically.
@@ -0,0 +1,93 @@
1
+ # Contributing to arrayview
2
+
3
+ Thanks for your interest. This guide keeps things consistent as more people
4
+ contribute.
5
+
6
+ ## Proposing changes
7
+
8
+ For anything user-facing (new shortcut, overlay, layout change), **open an
9
+ issue first**. Include:
10
+
11
+ - What it does, in one sentence
12
+ - Which key triggers it (if any)
13
+ - Which modes it affects (Normal, Multi-view, Compare, Diff, Registration, qMRI)
14
+ - A rough sketch or description of how it looks
15
+
16
+ Bug fixes and internal refactors can go straight to a PR.
17
+
18
+ ## Design principles
19
+
20
+ 1. **Array fills the screen.** Minimize chrome. UI elements stay hidden or
21
+ dimmed until the user hovers or presses a key, then fade back out.
22
+
23
+ 2. **Monospace only.** All text uses the system monospace stack
24
+ (`'SF Mono', ui-monospace, 'Cascadia Code', 'JetBrains Mono', monospace`).
25
+ Never use sans-serif.
26
+
27
+ 3. **Colors via CSS custom properties.** Use `var(--surface)`, `var(--text)`,
28
+ `var(--active-dim)`, etc. Never hardcode hex values. The viewer ships four
29
+ themes (dark, light, solarized, nord) and all must work.
30
+
31
+ 4. **Yellow for active state.** `--active-dim` (#f5c842 in dark theme) marks
32
+ the currently active element. Don't introduce new accent colors.
33
+
34
+ 5. **All six modes.** Every visual feature must be tested across Normal,
35
+ Multi-view (V/v), Compare (B/P), Diff (X), Registration (R), and qMRI (q).
36
+ If your feature only applies to some modes, add explicit mode guards.
37
+
38
+ ## Keyboard shortcuts
39
+
40
+ - Check the existing shortcut table (press `?` in the viewer) before picking a
41
+ key. Conflicts will be caught in review but save yourself the round-trip.
42
+ - Single lowercase letters are scarce. Prefer Shift+key or a modifier for new
43
+ features.
44
+ - If a shortcut only makes sense in certain modes, guard it:
45
+ ```js
46
+ if (currentMode !== 'compare') return;
47
+ ```
48
+ - Document the new shortcut in the help overlay (`#help-overlay` in
49
+ `_viewer.html`).
50
+
51
+ ## Popup menus and overlays
52
+
53
+ Several proposals involve popup/context menus. To keep them visually
54
+ consistent:
55
+
56
+ - Background: `var(--surface)`, border: `1px solid var(--border)`,
57
+ border-radius: `var(--radius-lg)`.
58
+ - Dismiss on **Escape** and on clicking outside the popup.
59
+ - No permanent visibility -- show on trigger, hide when done.
60
+ - Keep text small (12-13px) and monospace.
61
+ - Use `var(--active-dim)` for the selected/hovered item, `var(--text)` for
62
+ normal items, `var(--muted)` for secondary info.
63
+ - Animate in with a short opacity+scale transition, not an instant pop.
64
+
65
+ Look at `#uni-picker-box` in `_viewer.html` for a reference implementation.
66
+
67
+ ## Testing checklist
68
+
69
+ Before submitting a PR:
70
+
71
+ - [ ] `uv run pytest tests/test_api.py -x` passes
72
+ - [ ] `uv run python tests/visual_smoke.py` passes
73
+ - [ ] If you added UI, update `tests/visual_smoke.py` to cover it
74
+ - [ ] Manually verify in all affected modes (at minimum: Normal + one
75
+ multi-pane mode)
76
+ - [ ] New shortcuts are documented in the help overlay
77
+
78
+ ## Dev setup
79
+
80
+ ```bash
81
+ git clone <repo-url>
82
+ cd arrayview
83
+ uv sync
84
+ uv run arrayview tests/ # launch with test data
85
+ ```
86
+
87
+ ## Style notes
88
+
89
+ - The frontend lives in a single file: `src/arrayview/_viewer.html`.
90
+ HTML, CSS, and JS are all in there. Keep it that way.
91
+ - Python backend uses `uv` for package management.
92
+ - Commit messages follow conventional commits (`feat:`, `fix:`, `refactor:`,
93
+ etc.).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: arrayview
3
- Version: 0.12.0
3
+ Version: 0.12.1
4
4
  Summary: Fast multi-dimensional array viewer
5
5
  Project-URL: Home, https://github.com/oscarvanderheide/arrayview
6
6
  Project-URL: Source, https://github.com/oscarvanderheide/arrayview
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "arrayview"
7
- version = "0.12.0"
7
+ version = "0.12.1"
8
8
  description = "Fast multi-dimensional array viewer"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.12"
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # ---------------------------------------------------------------------------
5
+ # release.sh — bump version, commit, tag, push, create GitHub release
6
+ # ---------------------------------------------------------------------------
7
+
8
+ BUMP="minor"
9
+ DRY_RUN=true
10
+ NO_AI=false
11
+
12
+ usage() {
13
+ cat <<EOF
14
+ Usage: $(basename "$0") [OPTIONS]
15
+
16
+ Options:
17
+ --bump {major,minor,patch} Version bump type (default: minor)
18
+ --execute Actually run (default is dry-run)
19
+ --no-ai Skip AI release notes, use GitHub's --generate-notes
20
+ -h, --help Show this help
21
+ EOF
22
+ }
23
+
24
+ while [[ $# -gt 0 ]]; do
25
+ case "$1" in
26
+ --bump) BUMP="$2"; shift 2 ;;
27
+ --execute) DRY_RUN=false; shift ;;
28
+ --no-ai) NO_AI=true; shift ;;
29
+ -h|--help) usage; exit 0 ;;
30
+ *) echo "Unknown option: $1"; usage; exit 1 ;;
31
+ esac
32
+ done
33
+
34
+ if [[ ! "$BUMP" =~ ^(major|minor|patch)$ ]]; then
35
+ echo "Error: --bump must be major, minor, or patch (got '$BUMP')"
36
+ exit 1
37
+ fi
38
+
39
+ # --- Guard: clean working tree on main ---
40
+ branch=$(git rev-parse --abbrev-ref HEAD)
41
+ if [[ "$branch" != "main" ]]; then
42
+ echo "Error: must be on main (currently on '$branch')"
43
+ exit 1
44
+ fi
45
+
46
+ if ! git diff --quiet || ! git diff --cached --quiet; then
47
+ echo "Error: working tree is dirty — commit or stash first"
48
+ exit 1
49
+ fi
50
+
51
+ # --- Bump version ---
52
+ uv version --bump "$BUMP"
53
+ VERSION=$(uv version --short)
54
+ TAG="v${VERSION}"
55
+
56
+ echo "Version bumped to $VERSION (tag: $TAG)"
57
+
58
+ uv lock --quiet
59
+
60
+ run() {
61
+ echo "+ $*"
62
+ if [[ "$DRY_RUN" == true ]]; then
63
+ return
64
+ fi
65
+ "$@"
66
+ }
67
+
68
+ # --- Generate release notes ---
69
+ CLAUDE_BIN="${CLAUDE_BIN:-/Users/oscar/.local/bin/claude}"
70
+ PREV_TAG=$(git describe --tags --abbrev=0 HEAD 2>/dev/null || echo "")
71
+ NOTES=""
72
+
73
+ if [[ -n "$PREV_TAG" ]]; then
74
+ COMMITS=$(git log "${PREV_TAG}..HEAD" --oneline)
75
+ else
76
+ COMMITS=$(git log --oneline -20)
77
+ fi
78
+
79
+ if [[ "$NO_AI" == false ]] && command -v "$CLAUDE_BIN" &>/dev/null; then
80
+ echo "Generating release notes with Claude..."
81
+ NOTES=$("$CLAUDE_BIN" -p "You are writing release notes for arrayview $TAG (a Python array/image viewer).
82
+
83
+ Here are the commits since the last release ($PREV_TAG):
84
+
85
+ $COMMITS
86
+
87
+ Write concise, user-friendly release notes in this exact format:
88
+
89
+ ## What's new in $TAG
90
+
91
+ - **Feature name**: one-sentence description
92
+
93
+ Rules:
94
+ - 5-10 bullet points max — group related commits into one bullet
95
+ - Write for end-users, not developers (no commit hashes, no file names)
96
+ - Use past tense (\"added\", \"fixed\", \"improved\")
97
+ - Skip pure refactors/docs unless they affect user experience
98
+ - Bold the feature name, keep the description to one sentence" 2>/dev/null) || true
99
+ fi
100
+
101
+ if [[ -z "$NOTES" ]]; then
102
+ if [[ "$NO_AI" == false ]]; then
103
+ echo "AI notes unavailable, falling back to --generate-notes"
104
+ fi
105
+ fi
106
+
107
+ if [[ "$DRY_RUN" == true && -n "$NOTES" ]]; then
108
+ echo ""
109
+ echo "--- Release notes preview ---"
110
+ echo "$NOTES"
111
+ echo "-----------------------------"
112
+ echo ""
113
+ fi
114
+
115
+ # --- Commit, tag, push, release ---
116
+ run git add pyproject.toml uv.lock
117
+ run git commit -m "release: $TAG"
118
+ run git push origin main
119
+ run git tag "$TAG"
120
+ run git push origin "$TAG"
121
+
122
+ if [[ -n "$NOTES" ]]; then
123
+ run gh release create "$TAG" \
124
+ --title "$TAG" \
125
+ --notes "$NOTES" \
126
+ --prerelease
127
+ else
128
+ run gh release create "$TAG" \
129
+ --title "$TAG" \
130
+ --generate-notes \
131
+ --prerelease
132
+ fi
133
+
134
+ if [[ "$DRY_RUN" == true ]]; then
135
+ echo ""
136
+ echo "(dry-run — re-run with --execute to apply)"
137
+ git checkout pyproject.toml uv.lock
138
+ fi
@@ -44,7 +44,7 @@ wheels = [
44
44
 
45
45
  [[package]]
46
46
  name = "arrayview"
47
- version = "0.11.0"
47
+ version = "0.12.1"
48
48
  source = { editable = "." }
49
49
  dependencies = [
50
50
  { name = "fastapi" },
@@ -1,343 +0,0 @@
1
- const vscode = require('vscode');
2
- const fs = require('fs');
3
- const path = require('path');
4
- const os = require('os');
5
-
6
- const SIGNAL_DIR = path.join(os.homedir(), '.arrayview');
7
- const SIGNAL_FILE = path.join(SIGNAL_DIR, 'open-request-v0310.json');
8
- const LOG_FILE = path.join(SIGNAL_DIR, 'extension.log');
9
-
10
- let _version = 'unknown';
11
- let _panel;
12
-
13
- function log(msg) {
14
- const line = `[${new Date().toISOString()}] ${msg}\n`;
15
- try { fs.appendFileSync(LOG_FILE, line); } catch (_) {}
16
- console.log(`[arrayview-opener] ${msg}`);
17
- }
18
-
19
- function getPanelColumn() {
20
- return vscode.window.activeTextEditor?.viewColumn || vscode.ViewColumn.Active;
21
- }
22
-
23
- function buildCandidateUrls(url) {
24
- const first = new URL(url);
25
- const candidates = [];
26
-
27
- if (!first.searchParams.has('transport')) {
28
- first.searchParams.set('transport', 'http');
29
- }
30
- candidates.push(first.toString());
31
-
32
- if (first.hostname === 'localhost') {
33
- const alt = new URL(first.toString());
34
- alt.hostname = '127.0.0.1';
35
- candidates.push(alt.toString());
36
- }
37
-
38
- return candidates;
39
- }
40
-
41
- async function openInSimpleBrowser(url, reason) {
42
- log(`REMOTE: simpleBrowser fallback starting reason=${reason} url=${url}`);
43
- try {
44
- let finalUrl = url;
45
- try {
46
- const original = new URL(url);
47
- const resolved = await vscode.env.asExternalUri(vscode.Uri.parse(url));
48
- const resolvedUrl = resolved.toString();
49
- log(`REMOTE: asExternalUri resolved reason=${reason} input=${url} resolved=${resolvedUrl}`);
50
- if (resolvedUrl) {
51
- const resolvedParsed = new URL(resolvedUrl);
52
- resolvedParsed.pathname = original.pathname || '/';
53
- resolvedParsed.search = original.search;
54
- resolvedParsed.hash = original.hash;
55
- finalUrl = resolvedParsed.toString();
56
- log(`REMOTE: reconstructed fallback url reason=${reason} finalUrl=${finalUrl}`);
57
- }
58
- } catch (e) {
59
- log(`REMOTE: asExternalUri FAILED reason=${reason} input=${url} error=${e.message}`);
60
- }
61
-
62
- await vscode.commands.executeCommand('simpleBrowser.show', finalUrl);
63
- log(`REMOTE: simpleBrowser fallback completed reason=${reason} finalUrl=${finalUrl}`);
64
- return true;
65
- } catch (e) {
66
- log(`REMOTE: simpleBrowser fallback FAILED reason=${reason} url=${url} error=${e.message}`);
67
- return false;
68
- }
69
- }
70
-
71
- function createPanel(url) {
72
- const parsed = new URL(url);
73
- const port = Number(parsed.port || '80');
74
- const candidateUrls = buildCandidateUrls(url);
75
- const title = 'ArrayView';
76
-
77
- if (_panel) {
78
- _panel.title = title;
79
- _panel.webview.options = {
80
- enableScripts: true,
81
- retainContextWhenHidden: true,
82
- portMapping: [{ webviewPort: port, extensionHostPort: port }],
83
- };
84
- _panel.webview.html = getWebviewHtml(candidateUrls, port);
85
- _panel.reveal(getPanelColumn(), true);
86
- return _panel;
87
- }
88
-
89
- _panel = vscode.window.createWebviewPanel(
90
- 'arrayview',
91
- title,
92
- getPanelColumn(),
93
- {
94
- enableScripts: true,
95
- retainContextWhenHidden: true,
96
- portMapping: [{ webviewPort: port, extensionHostPort: port }],
97
- }
98
- );
99
- _panel.onDidDispose(() => {
100
- _panel = undefined;
101
- });
102
- _panel.webview.onDidReceiveMessage((message) => {
103
- if (!message) {
104
- return;
105
- }
106
- if (message.type === 'log') {
107
- log(`REMOTE BOOTSTRAP: ${message.message}`);
108
- return;
109
- }
110
- if (message.type === 'viewer-loaded') {
111
- log('REMOTE: viewer iframe loaded');
112
- return;
113
- }
114
- if (message.type === 'viewer-error') {
115
- log(`REMOTE: viewer iframe error: ${message.message}`);
116
- return;
117
- }
118
- if (message.type === 'viewer-phase') {
119
- const detail = message.detail ? ` ${JSON.stringify(message.detail)}` : '';
120
- log(`REMOTE: viewer phase=${message.phase}${detail}`);
121
- return;
122
- }
123
- if (message.type === 'open-simple-browser') {
124
- openInSimpleBrowser(message.url, message.reason || 'webview-timeout');
125
- }
126
- });
127
- _panel.webview.html = getWebviewHtml(candidateUrls, port);
128
- return _panel;
129
- }
130
-
131
- function getWebviewHtml(urls, port) {
132
- const escapedUrls = JSON.stringify(urls);
133
- return `<!DOCTYPE html>
134
- <html lang="en">
135
- <head>
136
- <meta charset="UTF-8">
137
- <meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src http://localhost:${port} https://localhost:${port} http://127.0.0.1:${port} https://127.0.0.1:${port}; script-src 'unsafe-inline'; style-src 'unsafe-inline';">
138
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
139
- <style>
140
- html, body {
141
- margin: 0;
142
- padding: 0;
143
- width: 100%;
144
- height: 100%;
145
- overflow: hidden;
146
- background: #111;
147
- color: #f3f3f3;
148
- font-family: -apple-system, BlinkMacSystemFont, sans-serif;
149
- }
150
- body {
151
- display: flex;
152
- align-items: center;
153
- justify-content: center;
154
- }
155
- #status {
156
- font-size: 16px;
157
- letter-spacing: 0.04em;
158
- text-transform: none;
159
- opacity: 0.95;
160
- max-width: 560px;
161
- line-height: 1.5;
162
- text-align: center;
163
- padding: 24px;
164
- }
165
- iframe {
166
- width: 100%;
167
- height: 100%;
168
- border: 0;
169
- display: none;
170
- }
171
- </style>
172
- </head>
173
- <body>
174
- <div id="status">Connecting ArrayView...</div>
175
- <iframe id="viewer" referrerpolicy="no-referrer"></iframe>
176
- <script>
177
- const vscode = acquireVsCodeApi();
178
- const candidateUrls = ${escapedUrls};
179
- const status = document.getElementById('status');
180
- const viewer = document.getElementById('viewer');
181
- let candidateIndex = -1;
182
- let viewerReady = false;
183
- let fallbackTimer = null;
184
-
185
- function report(message) {
186
- try {
187
- vscode.postMessage({ type: 'log', message });
188
- } catch (_) {
189
- }
190
- }
191
-
192
- window.addEventListener('error', (event) => {
193
- report('window error: ' + event.message);
194
- status.textContent = 'ArrayView bootstrap error: ' + event.message;
195
- });
196
-
197
- viewer.addEventListener('load', () => {
198
- report('viewer iframe load event url=' + viewer.src);
199
- vscode.postMessage({ type: 'viewer-loaded' });
200
- });
201
-
202
- viewer.addEventListener('error', () => {
203
- const message = 'viewer iframe failed to load';
204
- report(message);
205
- status.textContent = 'ArrayView failed to load inside the remote preview.';
206
- vscode.postMessage({ type: 'viewer-error', message });
207
- });
208
-
209
- window.addEventListener('message', (event) => {
210
- const data = event.data;
211
- if (!data || data.source !== 'arrayview-viewer') {
212
- return;
213
- }
214
- vscode.postMessage({ type: 'viewer-phase', phase: data.phase, detail: data.detail || null });
215
- if (data.phase === 'frame-rendered') {
216
- viewerReady = true;
217
- status.style.display = 'none';
218
- viewer.style.display = 'block';
219
- if (fallbackTimer) {
220
- clearTimeout(fallbackTimer);
221
- fallbackTimer = null;
222
- }
223
- }
224
- });
225
-
226
- function scheduleFallback() {
227
- if (fallbackTimer) {
228
- clearTimeout(fallbackTimer);
229
- }
230
- fallbackTimer = window.setTimeout(() => {
231
- if (viewerReady) {
232
- return;
233
- }
234
- if (candidateIndex + 1 < candidateUrls.length) {
235
- report('viewer not ready; trying fallback candidate');
236
- loadViewer('fallback');
237
- return;
238
- }
239
- status.textContent = 'ArrayView opened, but no frame rendered yet.';
240
- report('viewer did not report a rendered frame before timeout');
241
- try {
242
- vscode.postMessage({
243
- type: 'open-simple-browser',
244
- url: candidateUrls[0],
245
- reason: 'no-frame-rendered',
246
- });
247
- } catch (_) {
248
- }
249
- }, 5000);
250
- }
251
-
252
- function loadViewer(reason) {
253
- candidateIndex += 1;
254
- const viewerUrl = candidateUrls[candidateIndex];
255
- viewerReady = false;
256
- report('loading viewer iframe=' + viewerUrl + ' reason=' + reason + ' candidate=' + (candidateIndex + 1) + '/' + candidateUrls.length);
257
- status.style.display = 'block';
258
- status.textContent = 'Loading ArrayView...';
259
- viewer.src = viewerUrl;
260
- viewer.style.display = 'block';
261
- scheduleFallback();
262
- }
263
-
264
- report('webview bootstrap ready candidates=' + candidateUrls.join(','));
265
- loadViewer('initial');
266
- </script>
267
- </body>
268
- </html>`;
269
- }
270
-
271
- async function tryOpenSignalFile() {
272
- try {
273
- if (!fs.existsSync(SIGNAL_FILE)) return;
274
- log('SIGNAL: found signal file');
275
- const raw = fs.readFileSync(SIGNAL_FILE, 'utf8');
276
- try { fs.unlinkSync(SIGNAL_FILE); } catch (e) { log(`SIGNAL: unlink failed: ${e.message}`); }
277
-
278
- let data;
279
- try { data = JSON.parse(raw); } catch (e) { log(`SIGNAL: JSON parse failed: ${e.message}`); return; }
280
-
281
- const url = data.url;
282
- if (!url) { log('SIGNAL: no url in signal file'); return; }
283
-
284
- log(`SIGNAL: url=${url}`);
285
-
286
- if (vscode.env.remoteName) {
287
- log(`REMOTE: opening webview panel for ${url}`);
288
- try {
289
- createPanel(url);
290
- log('REMOTE: webview panel opened');
291
- } catch (e) {
292
- log(`REMOTE: webview FAILED: ${e.message}`);
293
- }
294
- } else {
295
- log(`LOCAL: calling simpleBrowser.show(${url})...`);
296
- try {
297
- await vscode.commands.executeCommand('simpleBrowser.show', url);
298
- log('LOCAL: simpleBrowser.show completed');
299
- } catch (e) {
300
- log(`LOCAL: simpleBrowser.show FAILED: ${e.message}`);
301
- }
302
- }
303
- } catch (e) {
304
- log(`ERROR in tryOpenSignalFile: ${e.message}\n${e.stack}`);
305
- }
306
- }
307
-
308
- function activate(context) {
309
- _version = context.extension.packageJSON.version;
310
- log(`=== ACTIVATE v${_version} ===`);
311
- log(`remoteName=${vscode.env.remoteName} appHost=${vscode.env.appHost}`);
312
-
313
- try { fs.mkdirSync(SIGNAL_DIR, { recursive: true }); } catch (_) {}
314
-
315
- // Check for a signal file that arrived before we activated
316
- tryOpenSignalFile();
317
-
318
- // Poll every 1s (safety net)
319
- const interval = setInterval(() => tryOpenSignalFile(), 1000);
320
- context.subscriptions.push({ dispose: () => clearInterval(interval) });
321
-
322
- // fs.watch for faster response
323
- try {
324
- const watcher = fs.watch(SIGNAL_DIR, (eventType, filename) => {
325
- if (filename === path.basename(SIGNAL_FILE)) {
326
- log(`WATCH: event=${eventType} file=${filename}`);
327
- setTimeout(() => tryOpenSignalFile(), 100);
328
- }
329
- });
330
- context.subscriptions.push({ dispose: () => watcher.close() });
331
- log(`WATCH: fs.watch active on ${SIGNAL_DIR}`);
332
- } catch (e) {
333
- log(`WATCH: fs.watch failed (polling still active): ${e.message}`);
334
- }
335
-
336
- log('=== ACTIVATE DONE ===');
337
- }
338
-
339
- function deactivate() {
340
- log(`deactivate v${_version}`);
341
- }
342
-
343
- module.exports = { activate, deactivate };
@@ -1,13 +0,0 @@
1
- {
2
- "name": "arrayview-opener",
3
- "displayName": "ArrayView Opener",
4
- "description": "Opens ArrayView URLs in VS Code Simple Browser",
5
- "version": "0.3.10",
6
- "publisher": "arrayview",
7
- "engines": { "vscode": "^1.80.0" },
8
- "extensionKind": ["workspace"],
9
- "categories": ["Other"],
10
- "activationEvents": ["*"],
11
- "main": "./extension.js",
12
- "contributes": {}
13
- }
@@ -1,78 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
-
4
- # ---------------------------------------------------------------------------
5
- # release.sh — bump version, commit, tag, push, create GitHub release
6
- # ---------------------------------------------------------------------------
7
-
8
- BUMP="minor"
9
- DRY_RUN=true
10
-
11
- usage() {
12
- cat <<EOF
13
- Usage: $(basename "$0") [OPTIONS]
14
-
15
- Options:
16
- --bump {major,minor,patch} Version bump type (default: minor)
17
- --execute Actually run (default is dry-run)
18
- -h, --help Show this help
19
- EOF
20
- }
21
-
22
- while [[ $# -gt 0 ]]; do
23
- case "$1" in
24
- --bump) BUMP="$2"; shift 2 ;;
25
- --execute) DRY_RUN=false; shift ;;
26
- -h|--help) usage; exit 0 ;;
27
- *) echo "Unknown option: $1"; usage; exit 1 ;;
28
- esac
29
- done
30
-
31
- if [[ ! "$BUMP" =~ ^(major|minor|patch)$ ]]; then
32
- echo "Error: --bump must be major, minor, or patch (got '$BUMP')"
33
- exit 1
34
- fi
35
-
36
- # --- Guard: clean working tree on main ---
37
- branch=$(git rev-parse --abbrev-ref HEAD)
38
- if [[ "$branch" != "main" ]]; then
39
- echo "Error: must be on main (currently on '$branch')"
40
- exit 1
41
- fi
42
-
43
- if ! git diff --quiet || ! git diff --cached --quiet; then
44
- echo "Error: working tree is dirty — commit or stash first"
45
- exit 1
46
- fi
47
-
48
- # --- Bump version ---
49
- uv version --bump "$BUMP"
50
- VERSION=$(uv version --short)
51
- TAG="v${VERSION}"
52
-
53
- echo "Version bumped to $VERSION (tag: $TAG)"
54
-
55
- run() {
56
- echo "+ $*"
57
- if [[ "$DRY_RUN" == true ]]; then
58
- return
59
- fi
60
- "$@"
61
- }
62
-
63
- # --- Commit, tag, push, release ---
64
- run git add pyproject.toml
65
- run git commit -m "release: $TAG"
66
- run git push origin main
67
- run git tag "$TAG"
68
- run git push origin "$TAG"
69
- run gh release create "$TAG" \
70
- --title "[Pre-release] $TAG" \
71
- --generate-notes \
72
- --prerelease
73
-
74
- if [[ "$DRY_RUN" == true ]]; then
75
- echo ""
76
- echo "(dry-run — re-run with --execute to apply)"
77
- git checkout pyproject.toml
78
- fi
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes