diff --git a/.oxfmtrc.json b/.oxfmtrc.json
new file mode 100644
index 0000000..8d2b421
--- /dev/null
+++ b/.oxfmtrc.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "./node_modules/oxfmt/configuration_schema.json",
+ "semi": false,
+ "singleQuote": false,
+ "ignorePatterns": []
+}
diff --git a/.oxlintrc.json b/.oxlintrc.json
new file mode 100644
index 0000000..9d88688
--- /dev/null
+++ b/.oxlintrc.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "./node_modules/oxlint/configuration_schema.json",
+ "plugins": ["typescript", "oxc"],
+ "env": {
+ "browser": true,
+ "es2024": true
+ },
+ "categories": {
+ "correctness": "error"
+ }
+}
diff --git a/CONTEXT.md b/CONTEXT.md
index 6ffd698..8a143a1 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -5,36 +5,45 @@ Ubiquitous language for the product-radar app. Terms here should appear verbatim
## Glossary
### Radar
+
The top-level entity. A user creates a Radar, names it, defines its **Criteria** (3–7), and adds its **Profiles** (up to 5). A Radar is the unit of save / open / delete / share.
-- **Aliases to avoid:** *chart* (the chart is the visualization of the Radar, not the Radar itself), *comparison* (we use Radar even when only one Profile is plotted).
+- **Aliases to avoid:** _chart_ (the chart is the visualization of the Radar, not the Radar itself), _comparison_ (we use Radar even when only one Profile is plotted).
### Criterion
-A single named dimension on the radar chart (one "spoke"). Examples: *Price*, *Battery life*, *Ease of use*. A radar has **between 3 and 7 Criteria**. Flat — Criteria do not nest.
-- **Aliases to avoid:** *branch* (the user's original term — implies a hierarchy that does not exist), *axis* (acceptable but reserved for the geometric/visual concept, not the domain concept).
+A single named dimension on the radar chart (one "spoke"). Examples: _Price_, _Battery life_, _Ease of use_. A radar has **between 3 and 7 Criteria**. Flat — Criteria do not nest.
+
+- **Aliases to avoid:** _branch_ (the user's original term — implies a hierarchy that does not exist), _axis_ (acceptable but reserved for the geometric/visual concept, not the domain concept).
### Profile
-One labeled shape on the radar — a set of Scores, one per Criterion. Examples: *Us-Now*, *Us-Target*, *Competitor X*. A radar has **up to 5 Profiles** total. Neutral term covering both self-snapshots and competitors.
-- **Aliases to avoid:** *product* (too narrow — *Us-Target* isn't a product yet, it's an aspiration), *snapshot* (implies point-in-time, awkward for competitors).
+One labeled shape on the radar — a set of Scores, one per Criterion. Examples: _Us-Now_, _Us-Target_, _Competitor X_. A radar has **up to 5 Profiles** total. Neutral term covering both self-snapshots and competitors.
+
+- **Aliases to avoid:** _product_ (too narrow — _Us-Target_ isn't a product yet, it's an aspiration), _snapshot_ (implies point-in-time, awkward for competitors).
### Score
+
A single integer in **{0, 1, 2, 3, 4, 5}** assigned to one (Criterion, Profile) pair. 0 = worst on this Criterion; 5 = best. Every Profile must have a Score for every Criterion of its Radar — no nulls, no "N/A".
-- **Aliases to avoid:** *note* (French calque — use *Score*), *rating* (acceptable in user-facing copy, but *Score* is canonical in code).
+- **Aliases to avoid:** _note_ (French calque — use _Score_), _rating_ (acceptable in user-facing copy, but _Score_ is canonical in code).
+
+### Concept
+
+A short free-text subtitle on a Radar that names the product and helps the reader identify its typical performances at a glance. Optional — a Radar may have an empty Concept. Rendered under the Radar name (the title) on the chart, wrapping to a second line when long.
+
+- **Aliases to avoid:** _subtitle_ (that's the visual role, not the domain term), _description_ (too generic — a Concept is a tight positioning phrase, not prose), _tagline_ (acceptable in user-facing copy, but _Concept_ is canonical in code).
## Conventions
### Higher is better
-All Criteria must be phrased so that a higher Score is more desirable (e.g. *Affordability*, not *Price*; *Onboarding speed*, not *Time-to-onboard*). The app does not invert any axis. This is a naming discipline imposed on the user via UI hint text, not enforced by code.
+
+All Criteria must be phrased so that a higher Score is more desirable (e.g. _Affordability_, not _Price_; _Onboarding speed_, not _Time-to-onboard_). The app does not invert any axis. This is a naming discipline imposed on the user via UI hint text, not enforced by code.
Consequence: a larger Profile shape on the radar always means a "stronger" Profile, making visual comparison meaningful.
### Cardinality
+
- A Radar has **3 to 7 Criteria** (inclusive), and **1 to 5 Profiles** (inclusive).
- Within one Radar, Criterion names are unique. Profile names are unique.
- A Profile's Scores cover every Criterion in its Radar — exactly one Score per (Criterion, Profile) pair.
-
-
-
diff --git a/index.html b/index.html
index 003ec98..57268ea 100644
--- a/index.html
+++ b/index.html
@@ -6,7 +6,10 @@
-
+
product-radar
diff --git a/package.json b/package.json
index 0dcba29..49469d6 100644
--- a/package.json
+++ b/package.json
@@ -1,12 +1,16 @@
{
"name": "product-radar",
- "private": true,
"version": "0.0.0",
+ "private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "lint": "oxlint",
+ "lint:fix": "oxlint --fix",
+ "fmt": "oxfmt",
+ "fmt:check": "oxfmt --check"
},
"dependencies": {
"daisyui": "^5.5.23",
@@ -18,6 +22,8 @@
"@types/node": "^24.12.3",
"@vitejs/plugin-vue": "^6.0.6",
"@vue/tsconfig": "^0.9.1",
+ "oxfmt": "^0.55.0",
+ "oxlint": "^1.70.0",
"tailwindcss": "^4.3.1",
"typescript": "~6.0.2",
"vite": "^8.0.12",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index fb6fe64..b538d93 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -30,6 +30,12 @@ importers:
'@vue/tsconfig':
specifier: ^0.9.1
version: 0.9.1(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))
+ oxfmt:
+ specifier: ^0.55.0
+ version: 0.55.0
+ oxlint:
+ specifier: ^1.70.0
+ version: 1.70.0
tailwindcss:
specifier: ^4.3.1
version: 4.3.1
@@ -117,6 +123,250 @@ packages:
'@oxc-project/types@0.133.0':
resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==}
+ '@oxfmt/binding-android-arm-eabi@0.55.0':
+ resolution: {integrity: sha512-+rFDOqQe5LOWgxrAJaZgLRudr6GQm0wGI6gtu7vVkrdLGjNMUSGbAlaCr8j7F2H2Er97vYQCU8WDb30onqMM1g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxfmt/binding-android-arm64@0.55.0':
+ resolution: {integrity: sha512-ctulLq8s3x8Zmvw6+iccB09TIKERAklRSmbJ10gk8mlAn05qZxoyo52dj3Hi9IJcmDSwF54fQaTVh2CbL6PInw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxfmt/binding-darwin-arm64@0.55.0':
+ resolution: {integrity: sha512-xDQczLH9pw/RBk1h/GH0qcGMm8hQtmtVHBNLSH3lk1gEIR09hZ4L+mJQl4VqiVAvPK9VG9PYrWWuSQLt7xTbiA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxfmt/binding-darwin-x64@0.55.0':
+ resolution: {integrity: sha512-JaNoFCkF2CJdGgpPSMbuO9HVyXyoNGIhMHPvp6NYAjeVKw9XEYc0HcUWJLPQa3Q69WV5wMa9m5jPMJPtbLtcRg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxfmt/binding-freebsd-x64@0.55.0':
+ resolution: {integrity: sha512-DNbszhpg6S2MIzax5azdHFTTBIVkR5xr8yyRZuA4yoDAwOkzIp3tmldgKZM2+VlT+hJIG0xUksA+elISzMEAfA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxfmt/binding-linux-arm-gnueabihf@0.55.0':
+ resolution: {integrity: sha512-2snoaoRfFFyGnbOcKUK36rREBYxe/Xgz3uHbiA5zbCB/s6R4DQj4mHqYAaWWhgizCUSDxV8cE9zAZ0XleNpKGw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxfmt/binding-linux-arm-musleabihf@0.55.0':
+ resolution: {integrity: sha512-q1aktHF/WRpSK81BX1dE/9vWrS2jGw1Nax2kb4DBLGAewubCLcoNyp4Zl/NSMgbv3vUS46Z33wIQkBVYOP3PYg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxfmt/binding-linux-arm64-gnu@0.55.0':
+ resolution: {integrity: sha512-VD0y36aENezl/3tsclA/4G53Cc7iV+7Uoh7gz4yvcOTaEYBtJpQsE6PKDGTtUtOvGS4kv51ybfXY/nWZejO5IA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-arm64-musl@0.55.0':
+ resolution: {integrity: sha512-r8xlKJFcsRmn0H5jZrdORae6RX9jDBrZVvOoxF+bCQtampQJClv80aZEHsv+NsLsp2KCE5ql79O7DpPVzYWpXA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxfmt/binding-linux-ppc64-gnu@0.55.0':
+ resolution: {integrity: sha512-GRKv/HXHcwIVld/WU61rF0g0R16hl5EJ+ScKdpjevT57lnLnagj/U2YUbXf2mT+2Pg1uCzWC+mvGicPV3CDdLQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-riscv64-gnu@0.55.0':
+ resolution: {integrity: sha512-rdv57enTiPtpSYRMKfAiEbQb0Puw5t9N7isVinDoo5qeLDScro2gznmZqSgSWbVZRzLisTeCTW8Qwgw0bOHv3A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-riscv64-musl@0.55.0':
+ resolution: {integrity: sha512-7v1nNrlD43VY6+sYQ6efYyb3lE6QY182304PD/768ZxTjOmFd/3dQa3u/nGBUAXYdGSWOQc5N3PnS0QzUXyEIA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxfmt/binding-linux-s390x-gnu@0.55.0':
+ resolution: {integrity: sha512-f4lJLUSPOgScjFl9LiflKCTocyNRwE25JmTMbN4XQdDjoZzEHjqf3wA3VESF1/csg7i8m7+EQLbrZyYDqe10UQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-x64-gnu@0.55.0':
+ resolution: {integrity: sha512-MihqiPziJNoWy4MqNSV+jVA1g+07iQDjZiR0vaCaDoPgFEiJpCMsxamktzLV07cEeQsSJ04vQaU4CzCQwIvtDA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxfmt/binding-linux-x64-musl@0.55.0':
+ resolution: {integrity: sha512-Yqghym7KYAVjP9MmSrNZiDeerMuoejNjo0r3ox5H3GDKk8eAfl8VyJm9i+pWCLDCTnAbcTUMMN2ZKjUYXH1v3g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxfmt/binding-openharmony-arm64@0.55.0':
+ resolution: {integrity: sha512-s5SDvVVSbyQl1V5UU3Yl12M+XLUQ3rl5SglNqgAA2K4PXUtQhyNSS00wivONPEnNo5W01rCou8WkDNyvI/RGHg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxfmt/binding-win32-arm64-msvc@0.55.0':
+ resolution: {integrity: sha512-7p9FB5R32tw2KyyNX3wpQrR2WHwEHvMEiBlGXxeTCaRMCVNx3UtFMAUbaQ/pRNWIrEUZmYhJ6tcUH52uPTRYjQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxfmt/binding-win32-ia32-msvc@0.55.0':
+ resolution: {integrity: sha512-ZYqj3fDnOT1IaVGMP5kpmkQl4F3tQIm2ZyAxvqkJYmI0xgWWak4ss4XYwv3VDfM+TWXeC9K4uQ/wW5jm/5XABA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxfmt/binding-win32-x64-msvc@0.55.0':
+ resolution: {integrity: sha512-eEYT5tivGnGbPHuOHuQpi6CGLObhh0re/5jcNQHihD2GRYkTM85dyi5a19zjP8Q00t1uqAx+/QGLUGdHeqzWyg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@oxlint/binding-android-arm-eabi@1.70.0':
+ resolution: {integrity: sha512-zFh0P4cswmRvw6nkyb89dr18rRanuaCPAsEXsFDoQY8WdaquI8Pt4NWFjaMJg6L23cy5NeN8J9cBnREbWzZhaw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxlint/binding-android-arm64@1.70.0':
+ resolution: {integrity: sha512-qI8o4HZjeGiBrWv+pJv4lH0Yi2Gl/JSp/EumBUApezJprIKa5PS4nU0lQsQngtky8k+SplQIOjv6hwu0SSxeyg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxlint/binding-darwin-arm64@1.70.0':
+ resolution: {integrity: sha512-8KjgVVHI5F9nVwHCRwwA78Ty7zNKP4Wd9OeN5PSv3iu/F/u1RVXoOCgLhWqust6HmwQG6xc8c+RCyaWENy24+w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxlint/binding-darwin-x64@1.70.0':
+ resolution: {integrity: sha512-WVydssv5PSUBXFJTdNBWlmGkbNmvPGaFt/2SUT/EZRB6bq6bEOHmMlbnupZD5jmlEvi9+mZJHi8TCw15lyfSfQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxlint/binding-freebsd-x64@1.70.0':
+ resolution: {integrity: sha512-hJucmUf8OlinHNb1R7fI4Fw6WsAstOz7i8nmkWQfiHoZXtbufNm+MxiDTIMk1ggh2Ro4vLzgQ+bKvRY54MZoRA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.70.0':
+ resolution: {integrity: sha512-1BnS7wbCYDSXwWzJJ+mc3NURoha6m6m6RT5c6vgAY3oz7C3OVXP+S0awo2mRq97arrJkVvO3qRQfyAHL+76xtQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm-musleabihf@1.70.0':
+ resolution: {integrity: sha512-yKy/UdbR55+M2yEcuiV5DCNC/gdQAjr/GioUy50QwBzSrKm8ueWADqyRLS9Xk+qjNeCYGg6A8FvUBds56ttfqg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxlint/binding-linux-arm64-gnu@1.70.0':
+ resolution: {integrity: sha512-0A5XJ4alvmqFUFP/4oYSyaO+qLto/HrKEWTSaegiVl+HOufFngK2BjYw9x4RbwBt/du5QG6l5q1zeWiJYYG5yg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-arm64-musl@1.70.0':
+ resolution: {integrity: sha512-JiylyurlB0CLSedNtx1gzv3FvfWPF1h/2Y3BJszPLNt5XQFlBsH5ke0Jle3iJb3uqu5m2e7A/DwzpuCAHdiU+A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-linux-ppc64-gnu@1.70.0':
+ resolution: {integrity: sha512-J8VPG7I3/HmgaU4u8pNU2kFx2+0U+vPLS1dXFxXOaR/2TQ0f8AC7DRz0SRGRI1bfphnX2hVYTTtLuhL4nYKL+Q==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-riscv64-gnu@1.70.0':
+ resolution: {integrity: sha512-N2+4lV2KLN+oXTIIIwmWDhwkrnvqf5oX7Hw0zPjk+RuIVgiBQSOlJWF7uQoFx2siEYX0ZQ5cfSbEAHm+J3t7Wg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-riscv64-musl@1.70.0':
+ resolution: {integrity: sha512-1e2L7cFCvx9QDzq6NPP+0tABKb5z6nWHyddWTNKprEsjO9xNrAtPowuCGpjNXxkTdsMiZ4jc8YQ5SstZd4XK6g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-linux-s390x-gnu@1.70.0':
+ resolution: {integrity: sha512-Kwu/l/8GcYibCWA9m9N5pRXMIKVSsL/YbgpLzYkqDhWTiqdRfnNJ/+nqIKRKQiFbHWsdlHEhzMwruJK+qcEruA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-x64-gnu@1.70.0':
+ resolution: {integrity: sha512-tap04CsHYOl0nSAQJfPNIuBxqEPB2HnhQqwaOXLg1jnp2XfRo8Fa814dA4QC4zpvTWXCjAAaCY1W5LOORkEQuQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxlint/binding-linux-x64-musl@1.70.0':
+ resolution: {integrity: sha512-hzJa/WgvtJpbBD9rgfy0qe+MjbxOXNUT0bfR1S6EQQzfTtBFA9xg5q8KSwRrQ2QfSS+TaP4j+4mVPQrfNc6UNg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxlint/binding-openharmony-arm64@1.70.0':
+ resolution: {integrity: sha512-xbsaNSNzVSnaJACCUYr1HQMyY/Q/Q1LkePmHG3UvZPvGCYGNxrsZp9OmtA6ick8xH47ltRRbRrPCM1YXYcyC+A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxlint/binding-win32-arm64-msvc@1.70.0':
+ resolution: {integrity: sha512-icAEsUI7JbW1TMRdEXV83mVAInhRVQYuuAlPpxdGwJ95chNdnCzjloRW8GglT0WvzOEZSio6fnYSk2DJ2Hv7LQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxlint/binding-win32-ia32-msvc@1.70.0':
+ resolution: {integrity: sha512-FHMSWbVsPVs/f+Jcl04ws4JJ2wUnauyTzlpxWRG/lSO/8GpX08Fo2gQZqdA6CrRFI+zvkxl+N/KwJGWfUwYVZA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxlint/binding-win32-x64-msvc@1.70.0':
+ resolution: {integrity: sha512-ptOlKwCz7n4AKs5VweMqG6DAg677FmKOK+vBkkL9DMNgFATIQ+upqUYBTOEwRQyRAx1ncGlPlXleV2hIcm3z4g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
'@rolldown/binding-android-arm64@1.0.3':
resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -578,6 +828,32 @@ packages:
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
+ oxfmt@0.55.0:
+ resolution: {integrity: sha512-jSj2wCTakwgPMxkfiVZX0jf+nX+Nz6xlyAZjqNE0qXTFdCBPYlP6JAN+ODjmealw7DXBjOzYbdsqwBMAZnPZ6A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ svelte: ^5.0.0
+ vite-plus: '*'
+ peerDependenciesMeta:
+ svelte:
+ optional: true
+ vite-plus:
+ optional: true
+
+ oxlint@1.70.0:
+ resolution: {integrity: sha512-D6JgHtzkhRwvEC+A0Nw5AEc5bk8x5i1pHzvZIEf/a0C4hOzmAACNGtkDGPyFaxxX3ZVGxCPeig3P3rMM8XU3/g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+ peerDependencies:
+ oxlint-tsgolint: '>=0.22.1'
+ vite-plus: '*'
+ peerDependenciesMeta:
+ oxlint-tsgolint:
+ optional: true
+ vite-plus:
+ optional: true
+
path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
@@ -634,6 +910,10 @@ packages:
resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
engines: {node: '>=12.0.0'}
+ tinypool@2.1.0:
+ resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==}
+ engines: {node: ^20.0.0 || >=22.0.0}
+
tslib@2.8.1:
resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
@@ -823,6 +1103,120 @@ snapshots:
'@oxc-project/types@0.133.0': {}
+ '@oxfmt/binding-android-arm-eabi@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-android-arm64@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-darwin-arm64@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-darwin-x64@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-freebsd-x64@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm-gnueabihf@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm-musleabihf@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm64-gnu@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-arm64-musl@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-ppc64-gnu@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-riscv64-gnu@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-riscv64-musl@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-s390x-gnu@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-x64-gnu@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-linux-x64-musl@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-openharmony-arm64@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-win32-arm64-msvc@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-win32-ia32-msvc@0.55.0':
+ optional: true
+
+ '@oxfmt/binding-win32-x64-msvc@0.55.0':
+ optional: true
+
+ '@oxlint/binding-android-arm-eabi@1.70.0':
+ optional: true
+
+ '@oxlint/binding-android-arm64@1.70.0':
+ optional: true
+
+ '@oxlint/binding-darwin-arm64@1.70.0':
+ optional: true
+
+ '@oxlint/binding-darwin-x64@1.70.0':
+ optional: true
+
+ '@oxlint/binding-freebsd-x64@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-gnueabihf@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm-musleabihf@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-gnu@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-arm64-musl@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-ppc64-gnu@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-gnu@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-riscv64-musl@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-s390x-gnu@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-gnu@1.70.0':
+ optional: true
+
+ '@oxlint/binding-linux-x64-musl@1.70.0':
+ optional: true
+
+ '@oxlint/binding-openharmony-arm64@1.70.0':
+ optional: true
+
+ '@oxlint/binding-win32-arm64-msvc@1.70.0':
+ optional: true
+
+ '@oxlint/binding-win32-ia32-msvc@1.70.0':
+ optional: true
+
+ '@oxlint/binding-win32-x64-msvc@1.70.0':
+ optional: true
+
'@rolldown/binding-android-arm64@1.0.3':
optional: true
@@ -1196,6 +1590,52 @@ snapshots:
nanoid@3.3.12: {}
+ oxfmt@0.55.0:
+ dependencies:
+ tinypool: 2.1.0
+ optionalDependencies:
+ '@oxfmt/binding-android-arm-eabi': 0.55.0
+ '@oxfmt/binding-android-arm64': 0.55.0
+ '@oxfmt/binding-darwin-arm64': 0.55.0
+ '@oxfmt/binding-darwin-x64': 0.55.0
+ '@oxfmt/binding-freebsd-x64': 0.55.0
+ '@oxfmt/binding-linux-arm-gnueabihf': 0.55.0
+ '@oxfmt/binding-linux-arm-musleabihf': 0.55.0
+ '@oxfmt/binding-linux-arm64-gnu': 0.55.0
+ '@oxfmt/binding-linux-arm64-musl': 0.55.0
+ '@oxfmt/binding-linux-ppc64-gnu': 0.55.0
+ '@oxfmt/binding-linux-riscv64-gnu': 0.55.0
+ '@oxfmt/binding-linux-riscv64-musl': 0.55.0
+ '@oxfmt/binding-linux-s390x-gnu': 0.55.0
+ '@oxfmt/binding-linux-x64-gnu': 0.55.0
+ '@oxfmt/binding-linux-x64-musl': 0.55.0
+ '@oxfmt/binding-openharmony-arm64': 0.55.0
+ '@oxfmt/binding-win32-arm64-msvc': 0.55.0
+ '@oxfmt/binding-win32-ia32-msvc': 0.55.0
+ '@oxfmt/binding-win32-x64-msvc': 0.55.0
+
+ oxlint@1.70.0:
+ optionalDependencies:
+ '@oxlint/binding-android-arm-eabi': 1.70.0
+ '@oxlint/binding-android-arm64': 1.70.0
+ '@oxlint/binding-darwin-arm64': 1.70.0
+ '@oxlint/binding-darwin-x64': 1.70.0
+ '@oxlint/binding-freebsd-x64': 1.70.0
+ '@oxlint/binding-linux-arm-gnueabihf': 1.70.0
+ '@oxlint/binding-linux-arm-musleabihf': 1.70.0
+ '@oxlint/binding-linux-arm64-gnu': 1.70.0
+ '@oxlint/binding-linux-arm64-musl': 1.70.0
+ '@oxlint/binding-linux-ppc64-gnu': 1.70.0
+ '@oxlint/binding-linux-riscv64-gnu': 1.70.0
+ '@oxlint/binding-linux-riscv64-musl': 1.70.0
+ '@oxlint/binding-linux-s390x-gnu': 1.70.0
+ '@oxlint/binding-linux-x64-gnu': 1.70.0
+ '@oxlint/binding-linux-x64-musl': 1.70.0
+ '@oxlint/binding-openharmony-arm64': 1.70.0
+ '@oxlint/binding-win32-arm64-msvc': 1.70.0
+ '@oxlint/binding-win32-ia32-msvc': 1.70.0
+ '@oxlint/binding-win32-x64-msvc': 1.70.0
+
path-browserify@1.0.1: {}
pathe@2.0.3: {}
@@ -1262,6 +1702,8 @@ snapshots:
fdir: 6.5.0(picomatch@4.0.4)
picomatch: 4.0.4
+ tinypool@2.1.0: {}
+
tslib@2.8.1:
optional: true
diff --git a/src/App.vue b/src/App.vue
index b8a0cfa..7ea6fbf 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,5 +1,4 @@
-
+
@@ -8,7 +7,7 @@
product-radar
- browser-local · 0–5 scale
+ 0–5 scale
diff --git a/src/components/CriteriaEditor.vue b/src/components/CriteriaEditor.vue
index adb9bff..60c45d1 100644
--- a/src/components/CriteriaEditor.vue
+++ b/src/components/CriteriaEditor.vue
@@ -1,7 +1,7 @@
@@ -70,19 +76,29 @@ async function copyPng(): Promise {
-
-
+
+
← All radars
-
-
+
+
+
+
+
Copy PNG
Download PNG
@@ -90,10 +106,7 @@ async function copyPng(): Promise
{
-
diff --git a/src/views/RadarList.vue b/src/views/RadarList.vue
index e51bc38..73b116a 100644
--- a/src/views/RadarList.vue
+++ b/src/views/RadarList.vue
@@ -1,22 +1,20 @@
@@ -44,15 +42,10 @@ function formatDate(ts: number): string {
+ New radar
-
+
No radars yet.
-
- Create your first radar
-
+
Create your first radar
@@ -64,15 +57,13 @@ function formatDate(ts: number): string {
@click="open(r.id)"
>
-
{{ r.name || 'Untitled radar' }}
+
{{ r.name || "Untitled radar" }}
{{ r.criteria.length }} criteria · {{ r.profiles.length }} profile{{
- r.profiles.length === 1 ? '' : 's'
+ r.profiles.length === 1 ? "" : "s"
}}
-
- Updated {{ formatDate(r.updatedAt) }}
-
+
Updated {{ formatDate(r.updatedAt) }}