Compare commits

...

7 Commits

Author SHA1 Message Date
Gregory Schier
75dc82570b Rename BadgeButton to PillButton 2025-11-09 08:18:26 -08:00
Gregory Schier
d7a7a64ec4 New "Triangle" theme 2025-11-09 07:55:31 -08:00
Gregory Schier
3aae1b52d1 Update commercial use trial wording 2025-11-09 07:19:05 -08:00
Gregory Schier
9eddf716e1 Update commercial use trial wording 2025-11-09 07:07:18 -08:00
Gregory Schier
554e632c19 Minor license handling tweaks 2025-11-09 06:01:03 -08:00
Gregory Schier
054916b7af JSON linting 2025-11-08 15:24:31 -08:00
Gregory Schier
f2a63087b0 Actually fix GraphQLEditor.tsx properly 2025-11-06 09:33:12 -08:00
20 changed files with 397 additions and 134 deletions

177
package-lock.json generated
View File

@@ -18,8 +18,8 @@
"plugins/auth-basic",
"plugins/auth-bearer",
"plugins/auth-jwt",
"plugins/auth-oauth1",
"plugins/auth-oauth2",
"plugins/auth-oauth1",
"plugins/filter-jsonpath",
"plugins/filter-xpath",
"plugins/importer-curl",
@@ -112,7 +112,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
"integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.27.1",
@@ -401,7 +400,6 @@
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
"integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -1927,7 +1925,6 @@
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
@@ -1941,7 +1938,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -1951,7 +1947,6 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
@@ -1972,6 +1967,142 @@
"node": ">=14"
}
},
"node_modules/@prantlf/jsonlint": {
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/@prantlf/jsonlint/-/jsonlint-16.0.0.tgz",
"integrity": "sha512-L0jFtcsBRJZOr4T6sbePb1R6XYF6Nofj6kmEAxqTKCHEr50uvyxBFnB1UKaehWaMhHnvtyqWfTR5Go25RywXIQ==",
"license": "MIT",
"dependencies": {
"ajv": "8.17.1",
"ajv-draft-04": "1.0.0",
"cosmiconfig": "9.0.0",
"diff": "5.2.0",
"fast-glob": "3.3.2"
},
"bin": {
"jsonlint": "lib/cli.js"
},
"engines": {
"node": ">=16.9"
}
},
"node_modules/@prantlf/jsonlint/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/@prantlf/jsonlint/node_modules/ajv-draft-04": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
"integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
"license": "MIT",
"peerDependencies": {
"ajv": "^8.5.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/@prantlf/jsonlint/node_modules/cosmiconfig": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
"integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
"license": "MIT",
"dependencies": {
"env-paths": "^2.2.1",
"import-fresh": "^3.3.0",
"js-yaml": "^4.1.0",
"parse-json": "^5.2.0"
},
"engines": {
"node": ">=14"
},
"funding": {
"url": "https://github.com/sponsors/d-fischer"
},
"peerDependencies": {
"typescript": ">=4.9.5"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@prantlf/jsonlint/node_modules/diff": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
"integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/@prantlf/jsonlint/node_modules/fast-glob": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
"integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
"glob-parent": "^5.1.2",
"merge2": "^1.3.0",
"micromatch": "^4.0.4"
},
"engines": {
"node": ">=8.6.0"
}
},
"node_modules/@prantlf/jsonlint/node_modules/glob-parent": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@prantlf/jsonlint/node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/@prantlf/jsonlint/node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
"json-parse-even-better-errors": "^2.3.0",
"lines-and-columns": "^1.1.6"
},
"engines": {
"node": ">=8"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@replit/codemirror-emacs": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/@replit/codemirror-emacs/-/codemirror-emacs-6.1.0.tgz",
@@ -5111,7 +5242,6 @@
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
@@ -5306,7 +5436,6 @@
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -6851,11 +6980,19 @@
"url": "https://github.com/fb55/entities?sponsor=1"
}
},
"node_modules/env-paths": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/error-ex": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-arrayish": "^0.2.1"
@@ -7911,7 +8048,6 @@
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@@ -7967,7 +8103,6 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -9375,7 +9510,6 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"parent-module": "^1.0.0",
@@ -9572,7 +9706,6 @@
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"dev": true,
"license": "MIT"
},
"node_modules/is-async-function": {
@@ -9762,7 +9895,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -9816,7 +9948,6 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -9888,7 +10019,6 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
@@ -10275,7 +10405,6 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true,
"license": "MIT"
},
"node_modules/json-pointer": {
@@ -10793,7 +10922,6 @@
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true,
"license": "MIT"
},
"node_modules/liquid-json": {
@@ -11454,7 +11582,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -12043,7 +12170,6 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
"dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
@@ -13648,7 +13774,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"license": "MIT",
"dependencies": {
"callsites": "^3.0.0"
@@ -13853,14 +13978,12 @@
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
"dev": true,
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8.6"
@@ -14336,7 +14459,6 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
"dev": true,
"funding": [
{
"type": "github",
@@ -15146,7 +15268,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=4"
@@ -15183,7 +15304,6 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
"dev": true,
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
@@ -15282,7 +15402,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
"dev": true,
"funding": [
{
"type": "github",
@@ -17185,7 +17304,6 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
@@ -17419,7 +17537,7 @@
"version": "5.8.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"dev": true,
"devOptional": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
@@ -19070,6 +19188,7 @@
"@gilbarbara/deep-equal": "^0.3.1",
"@lezer/highlight": "^1.1.3",
"@lezer/lr": "^1.3.3",
"@prantlf/jsonlint": "^16.0.0",
"@replit/codemirror-emacs": "^6.1.0",
"@replit/codemirror-vim": "^6.3.0",
"@replit/codemirror-vscode-keymap": "^6.0.2",

View File

@@ -745,5 +745,47 @@ export const plugin: PluginDefinition = {
},
},
},
{
id: 'triangle',
dark: true,
label: 'Triangle',
base: {
surface: 'rgb(0,0,0)',
surfaceHighlight: 'rgb(21,21,21)',
surfaceActive: 'rgb(31,31,31)',
text: 'rgb(237,237,237)',
textSubtle: 'rgb(161,161,161)',
textSubtlest: 'rgb(115,115,115)',
border: 'rgb(31,31,31)',
primary: 'rgb(196,114,251)',
secondary: 'rgb(161,161,161)',
info: 'rgb(71,168,255)',
success: 'rgb(0,202,81)',
notice: 'rgb(255,175,0)',
warning: '#FF4C8D',
danger: '#fd495a',
},
components: {
editor: {
danger: '#FF4C8D',
warning: '#fd495a',
},
dialog: {
surface: 'rgb(10,10,10)',
border: 'rgb(31,31,31)',
},
sidebar: {
border: 'rgb(31,31,31)',
},
responsePane: {
surface: 'rgb(10,10,10)',
border: 'rgb(31,31,31)',
},
appHeader: {
surface: 'rgb(10,10,10)',
border: 'rgb(31,31,31)',
},
},
},
],
};

View File

@@ -1,5 +1,4 @@
use log::debug;
use mime_guess::{Mime, mime};
use mime_guess::{mime, Mime};
use std::path::Path;
use std::str::FromStr;
use tokio::fs;
@@ -7,14 +6,8 @@ use tokio::fs;
pub async fn read_response_body(body_path: impl AsRef<Path>, content_type: &str) -> Option<String> {
let body = fs::read(body_path).await.ok()?;
let body_charset = parse_charset(content_type).unwrap_or("utf-8".to_string());
debug!("body_charset: {}", body_charset);
if let Some(decoder) = charset::Charset::for_label(body_charset.as_bytes()) {
debug!("Using decoder for charset: {}", body_charset);
let (cow, real_encoding, exist_replace) = decoder.decode(&body);
debug!(
"Decoded body with charset: {}, real_encoding: {:?}, exist_replace: {}",
body_charset, real_encoding, exist_replace
);
let (cow, _real_encoding, _exist_replace) = decoder.decode(&body);
return cow.into_owned().into();
}

View File

@@ -1,22 +1,18 @@
use crate::error::Result;
use crate::{LicenseCheckStatus, activate_license, check_license, deactivate_license};
use log::{debug, info};
use tauri::{Runtime, WebviewWindow, command};
#[command]
pub async fn check<R: Runtime>(window: WebviewWindow<R>) -> Result<LicenseCheckStatus> {
debug!("Checking license");
check_license(&window).await
}
#[command]
pub async fn activate<R: Runtime>(license_key: &str, window: WebviewWindow<R>) -> Result<()> {
info!("Activating license {}", license_key);
activate_license(&window, license_key).await
}
#[command]
pub async fn deactivate<R: Runtime>(window: WebviewWindow<R>) -> Result<()> {
info!("Deactivating activation");
deactivate_license(&window).await
}

View File

@@ -67,6 +67,7 @@ pub async fn activate_license<R: Runtime>(
window: &WebviewWindow<R>,
license_key: &str,
) -> Result<()> {
info!("Activating license {}", license_key);
let client = reqwest::Client::new();
let payload = ActivateLicenseRequestPayload {
license_key: license_key.to_string(),
@@ -103,6 +104,7 @@ pub async fn activate_license<R: Runtime>(
}
pub async fn deactivate_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<()> {
info!("Deactivating activation");
let app_handle = window.app_handle();
let activation_id = get_activation_id(app_handle).await;
@@ -159,8 +161,6 @@ pub async fn check_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<Lice
let settings = window.db().get_settings();
let trial_end = settings.created_at.add(Duration::from_secs(TRIAL_SECONDS));
debug!("Trial ending at {trial_end:?}");
let has_activation_id = !activation_id.is_empty();
let trial_period_active = Utc::now().naive_utc() < trial_end;
@@ -185,11 +185,13 @@ pub async fn check_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<Lice
}
if response.status().is_server_error() {
warn!("Failed to check license {}", response.status());
return Err(ServerError);
}
let body: CheckActivationResponsePayload = response.json().await?;
if !body.active {
info!("Inactive License {:?}", body);
return Ok(LicenseCheckStatus::InvalidLicense);
}

View File

@@ -13,7 +13,7 @@ import {
setupOrConfigureEncryption,
withEncryptionEnabled,
} from '../lib/setupOrConfigureEncryption';
import { BadgeButton } from './core/BadgeButton';
import { PillButton } from './core/PillButton';
import { DismissibleBanner } from './core/DismissibleBanner';
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
import { Heading } from './core/Heading';
@@ -113,20 +113,20 @@ export function EnvironmentEditor({ environment, hideName, className, setRef }:
{!hideName && <div className="mr-2">{environment?.name}</div>}
{isEncryptionEnabled ? (
!allVariableAreEncrypted ? (
<BadgeButton color="notice" onClick={() => encryptEnvironment(environment)}>
<PillButton color="notice" onClick={() => encryptEnvironment(environment)}>
Encrypt All Variables
</BadgeButton>
</PillButton>
) : (
<BadgeButton color="secondary" onClick={setupOrConfigureEncryption}>
<PillButton color="secondary" onClick={setupOrConfigureEncryption}>
Encryption Settings
</BadgeButton>
</PillButton>
)
) : (
<BadgeButton color="secondary" onClick={() => valueVisibility.set((v) => !v)}>
<PillButton color="secondary" onClick={() => valueVisibility.set((v) => !v)}>
{valueVisibility.value ? 'Hide Values' : 'Show Values'}
</BadgeButton>
</PillButton>
)}
<BadgeButton
<PillButton
color="secondary"
rightSlot={<EnvironmentSharableTooltip />}
onClick={async () => {
@@ -134,7 +134,7 @@ export function EnvironmentEditor({ environment, hideName, className, setRef }:
}}
>
{environment.public ? 'Sharable' : 'Private'}
</BadgeButton>
</PillButton>
</Heading>
{environment.public && (!isEncryptionEnabled || !allVariableAreEncrypted) && (
<DismissibleBanner

View File

@@ -174,7 +174,6 @@ export function GrpcEditor({
language="json"
autocompleteFunctions
autocompleteVariables
forceUpdateKey={request.id}
defaultValue={request.message}
heightMode="auto"
placeholder="..."

View File

@@ -409,7 +409,6 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }:
) : activeRequest.bodyType === BODY_TYPE_GRAPHQL ? (
<Suspense>
<GraphQLEditor
key={forceUpdateKey}
forceUpdateKey={forceUpdateKey}
baseRequest={activeRequest}
request={activeRequest}

View File

@@ -5,7 +5,7 @@ import { useAtomValue } from 'jotai';
import type { ReactNode } from 'react';
import { openSettings } from '../commands/openSettings';
import { CargoFeature } from './CargoFeature';
import { BadgeButton } from './core/BadgeButton';
import { PillButton } from './core/PillButton';
import type { ButtonProps } from './core/Button';
const details: Record<
@@ -52,11 +52,11 @@ function LicenseBadgeCmp() {
}
return (
<BadgeButton
<PillButton
color={detail.color}
onClick={() => openSettings.mutate('license')}
>
{detail.label}
</BadgeButton>
</PillButton>
);
}

View File

@@ -10,6 +10,7 @@ import { Button } from '../core/Button';
import { Icon } from '../core/Icon';
import { Link } from '../core/Link';
import { PlainInput } from '../core/PlainInput';
import { Separator } from '../core/Separator';
import { HStack, VStack } from '../core/Stacks';
import { LocalImage } from '../LocalImage';
@@ -34,57 +35,70 @@ function SettingsLicenseCmp() {
<div className="flex flex-col gap-6 max-w-xl">
{check.data?.type === 'commercial_use' ? (
<Banner color="success">Your license is active 🥳</Banner>
) : check.data?.type == 'trialing' ? (
<Banner color="info" className="flex flex-col gap-3 max-w-lg">
<p>
<strong>
{pluralizeCount('day', differenceInDays(check.data.end, new Date()))} remaining
</strong>{' '}
on your commercial-use trial
</p>
</Banner>
) : check.data?.type == 'personal_use' ? (
<Banner color="notice" className="flex flex-col gap-3 max-w-lg">
<p>You are able to use Yaak for personal use only</p>
</Banner>
) : null}
{check.data?.type !== 'commercial_use' && (
<div className="grid grid-cols-[auto_minmax(0,1fr)] gap-6 items-center my-3 ">
<LocalImage src="static/greg.jpeg" className="rounded-full h-20 w-20" />
<div className="flex flex-col gap-2">
<h2 className="text-lg font-bold">Hey, I&apos;m Greg 👋🏼</h2>
<p>
Yaak is free for personal projects and learning.{' '}
{check.data?.type === 'trialing' ? 'Once your trial ends, a ' : 'A '}
license will be required for work or commercial use.
</p>
<p>
) : check.data?.type === 'trialing' ? (
<Banner color="info" className="@container flex items-center gap-x-5 max-w-xl">
<LocalImage src="static/greg.jpeg" className="hidden @sm:block rounded-full h-14 w-14" />
<p className="w-full">
<strong>{pluralizeCount('day', differenceInDays(check.data.end, new Date()))}</strong>{' '}
left to evaluate Yaak for commercial use.
<br />
<span className="opacity-50">Personal use is always free, forever.</span>
<Separator className="my-2" />
<div className="flex flex-wrap items-center gap-x-2 text-sm text-notice">
<Link noUnderline href="mailto:support@yaak.app">
Contact Support
</Link>
<Icon icon="dot" size="sm" color="secondary" />
<Link
noUnderline
href={`https://yaak.app/pricing?s=learn&t=${check.data?.type ?? ''}`}
className="text-sm text-notice opacity-80 hover:opacity-100"
>
Learn More
</Link>
</p>
</div>
</div>
)}
</div>
</p>
</Banner>
) : check.data?.type === 'personal_use' ? (
<Banner color="notice" className="@container flex items-center gap-x-5 max-w-xl">
<LocalImage src="static/greg.jpeg" className="hidden @sm:block rounded-full h-14 w-14" />
<p className="w-full">
Your commercial-use trial has ended.
<br />
<span className="opacity-50">
You may continue using Yaak for personal use free, forever.
<br />
A license is required for commercial use.
</span>
<Separator className="my-2" />
<div className="flex flex-wrap items-center gap-x-2 text-sm text-notice">
<Link noUnderline href="mailto:support@yaak.app">
Contact Support
</Link>
<Icon icon="dot" size="sm" color="secondary" />
<Link
noUnderline
href={`https://yaak.app/pricing?s=learn&t=${check.data?.type ?? ''}`}
>
Learn More
</Link>
</div>
</p>
</Banner>
) : null}
{check.error && <Banner color="danger">{check.error}</Banner>}
{activate.error && <Banner color="danger">{activate.error}</Banner>}
{check.data?.type === 'invalid_license' && (
<Banner color="danger">
Your license is invalid. Please <Link href="https://yaak.app/dashboard">Sign In</Link> for
more details
</Banner>
)}
{check.data?.type === 'commercial_use' ? (
<HStack space={2}>
<Button
variant="border"
color="secondary"
size="sm"
onClick={() => {
deactivate.mutate();
}}
>
<Button variant="border" color="secondary" size="sm" onClick={() => deactivate.mutate()}>
Deactivate License
</Button>
<Button
@@ -104,12 +118,12 @@ function SettingsLicenseCmp() {
<Button
size="sm"
color="primary"
rightSlot={<Icon icon="external_link" />}
onClick={() =>
openUrl(
`https://yaak.app/pricing?s=purchase&ref=app.yaak.desktop&t=${check.data?.type ?? ''}`,
)
}
rightSlot={<Icon icon="external_link" />}
>
Purchase License
</Button>

View File

@@ -6,7 +6,7 @@ import { useToggleCommandPalette } from '../hooks/useToggleCommandPalette';
import { workspaceLayoutAtom } from '../lib/atoms';
import { setupOrConfigureEncryption } from '../lib/setupOrConfigureEncryption';
import { CookieDropdown } from './CookieDropdown';
import { BadgeButton } from './core/BadgeButton';
import { PillButton } from './core/PillButton';
import { Icon } from './core/Icon';
import { IconButton } from './core/IconButton';
import { HStack } from './core/Stacks';
@@ -52,9 +52,9 @@ export const WorkspaceHeader = memo(function WorkspaceHeader({ className }: Prop
<div className="flex-1 flex gap-1 items-center h-full justify-end pointer-events-none pr-1">
<ImportCurlButton />
{showEncryptionSetup ? (
<BadgeButton color="danger" onClick={setupOrConfigureEncryption}>
<PillButton color="danger" onClick={setupOrConfigureEncryption}>
Enter Encryption Key
</BadgeButton>
</PillButton>
) : (
<LicenseBadge />
)}

View File

@@ -20,6 +20,7 @@
}
/* Matching bracket */
.cm-matchingBracket {
@apply bg-transparent border-b border-b-text-subtle;
}
@@ -76,6 +77,16 @@
.cm-gutterElement {
@apply cursor-default;
}
.cm-lint-marker {
@apply cursor-default opacity-80 hover:opacity-100 transition-opacity;
@apply rounded-full w-[0.9em] h-[0.9em];
content: '';
&.cm-lint-marker-error {
@apply bg-danger;
}
}
}
.template-tag {
@@ -110,6 +121,7 @@
@apply font-mono text-xs;
/* Hide scrollbars */
&::-webkit-scrollbar-corner,
&::-webkit-scrollbar {
@apply hidden !important;
@@ -202,7 +214,7 @@
}
.cm-editor .cm-activeLineGutter {
@apply bg-transparent;
@apply bg-transparent text-text-subtle;
}
/* Cursor and mouse cursor for readonly mode */
@@ -226,17 +238,35 @@
}
}
.cm-tooltip-lint {
@apply font-mono text-editor rounded overflow-hidden bg-surface-highlight border border-border shadow !important;
.cm-diagnostic-error {
@apply border-l-danger px-4 py-2;
}
}
.cm-lintPoint {
&.cm-lintPoint-error {
&::after {
@apply border-b-danger;
}
}
}
.cm-tooltip.cm-tooltip-hover {
@apply shadow-lg bg-surface rounded text-text-subtle border border-border-subtle z-50 pointer-events-auto text-sm;
@apply p-1.5;
/* Style the tooltip for popping up "open in browser" and other stuff */
a, button {
@apply text-text hover:bg-surface-highlight w-full h-sm flex items-center px-2 rounded;
}
a {
@apply cursor-default !important;
&::after {
@apply text-text bg-text h-3 w-3 ml-1;
content: '';

View File

@@ -19,7 +19,7 @@ import {
indentOnInput,
syntaxHighlighting,
} from '@codemirror/language';
import { lintKeymap } from '@codemirror/lint';
import { linter, lintGutter, lintKeymap } from '@codemirror/lint';
import { search, searchKeymap } from '@codemirror/search';
import type { Extension } from '@codemirror/state';
@@ -45,6 +45,7 @@ import { renderMarkdown } from '../../../lib/markdown';
import { pluralizeCount } from '../../../lib/pluralize';
import { showGraphQLDocExplorerAtom } from '../../graphql/graphqlAtoms';
import type { EditorProps } from './Editor';
import { jsonParseLinter } from './json-lint';
import { pairs } from './pairs/extension';
import { text } from './text/extension';
import type { TwigCompletionOption } from './twig/completion';
@@ -62,7 +63,7 @@ export const syntaxHighlightStyle = HighlightStyle.define([
textDecoration: 'underline',
},
{
tag: [t.paren, t.bracket, t.squareBracket, t.brace, t.separator, t.punctuation],
tag: [t.angleBracket, t.paren, t.bracket, t.squareBracket, t.brace, t.separator, t.punctuation],
color: 'var(--textSubtle)',
},
{
@@ -153,6 +154,10 @@ export function getLanguageExtension({
];
}
if (language === 'json') {
extraExtensions.push(linter(jsonParseLinter()), lintGutter());
}
const maybeBase = language ? syntaxExtensions[language] : null;
const base = typeof maybeBase === 'function' ? maybeBase() : null;
if (base == null) {

View File

@@ -0,0 +1,36 @@
import type { Diagnostic } from '@codemirror/lint';
import type { EditorView } from '@codemirror/view';
import { parse as jsonLintParse } from '@prantlf/jsonlint';
const TEMPLATE_SYNTAX_REGEX = /\$\{\[[\s\S]*?]}/g;
export function jsonParseLinter() {
return (view: EditorView): Diagnostic[] => {
try {
const doc = view.state.doc.toString();
// We need lint to not break on stuff like {"foo:" ${[ ... ]}} so we'll replace all template
// syntax with repeating `1` characters, so it's valid JSON and the position is still correct.
const escapedDoc = doc.replace(TEMPLATE_SYNTAX_REGEX, '1');
jsonLintParse(escapedDoc);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (err: any) {
if (!('location' in err)) {
return [];
}
// const line = location?.start?.line;
// const column = location?.start?.column;
if (err.location.start.offset) {
return [
{
from: err.location.start.offset,
to: err.location.start.offset,
severity: 'error',
message: err.message,
},
];
}
}
return [];
};
}

View File

@@ -12,32 +12,37 @@ interface Props extends HTMLAttributes<HTMLAnchorElement> {
export function Link({ href, children, noUnderline, className, ...other }: Props) {
const isExternal = href.match(/^https?:\/\//);
className = classNames(className, 'relative');
className = classNames(
className,
'relative',
'inline-flex items-center hover:underline group',
!noUnderline && 'underline',
);
if (isExternal) {
const isYaakLink = href.startsWith('https://yaak.app');
let finalHref = href;
if (href.startsWith('https://yaak.app')) {
if (isYaakLink) {
const url = new URL(href);
url.searchParams.set('ref', appInfo.identifier);
finalHref = url.toString();
}
return (
// eslint-disable-next-line react/jsx-no-target-blank
<a
href={finalHref}
target="_blank"
rel="noopener noreferrer"
className={classNames(
className,
'pr-4 inline-flex items-center hover:underline group',
!noUnderline && 'underline',
)}
onClick={(e) => {
e.preventDefault();
}}
rel={isYaakLink ? undefined : 'noopener noreferrer'}
onClick={(e) => e.preventDefault()}
className={className}
{...other}
>
<span className="pr-0.5">{children}</span>
<Icon className="inline absolute right-0.5 top-[0.3em] opacity-70 group-hover:opacity-100" size="xs" icon="external_link" />
<span className="pr-5">{children}</span>
<Icon
className="inline absolute right-0.5 top-[0.3em] opacity-70 group-hover:opacity-100"
size="xs"
icon="external_link"
/>
</a>
);
}

View File

@@ -2,7 +2,7 @@ import classNames from 'classnames';
import type { ButtonProps } from './Button';
import { Button } from './Button';
export function BadgeButton({ className, ...props }: ButtonProps) {
export function PillButton({ className, ...props }: ButtonProps) {
return (
<Button
size="2xs"

View File

@@ -1,3 +1,4 @@
import type { Color } from '@yaakapp-internal/plugins';
import classNames from 'classnames';
import type { ReactNode } from 'react';
@@ -6,9 +7,10 @@ interface Props {
dashed?: boolean;
className?: string;
children?: ReactNode;
color?: Color;
}
export function Separator({ className, dashed, orientation = 'horizontal', children }: Props) {
export function Separator({ color, className, dashed, orientation = 'horizontal', children }: Props) {
return (
<div role="separator" className={classNames(className, 'flex items-center w-full')}>
{children && (
@@ -16,7 +18,15 @@ export function Separator({ className, dashed, orientation = 'horizontal', child
)}
<div
className={classNames(
'h-0 border-t border-t-border-subtle',
'h-0 border-t opacity-60',
color == null && 'border-border',
color === 'primary' && 'border-primary',
color === 'secondary' && 'border-secondary',
color === 'success' && 'border-success',
color === 'notice' && 'border-notice',
color === 'warning' && 'border-warning',
color === 'danger' && 'border-danger',
color === 'info' && 'border-info',
dashed && 'border-dashed',
orientation === 'horizontal' && 'w-full h-[1px]',
orientation === 'vertical' && 'h-full w-[1px]',

View File

@@ -9,6 +9,7 @@ export interface TooltipProps {
content: ReactNode;
tabIndex?: number;
size?: 'md' | 'lg';
className?: string;
}
const hiddenStyles: CSSProperties = {
@@ -19,7 +20,7 @@ const hiddenStyles: CSSProperties = {
opacity: 0,
};
export function Tooltip({ children, content, tabIndex, size = 'md' }: TooltipProps) {
export function Tooltip({ children, className, content, tabIndex, size = 'md' }: TooltipProps) {
const [isOpen, setIsOpen] = useState<CSSProperties>();
const triggerRef = useRef<HTMLButtonElement>(null);
const tooltipRef = useRef<HTMLDivElement>(null);
@@ -94,8 +95,8 @@ export function Tooltip({ children, content, tabIndex, size = 'md' }: TooltipPro
ref={triggerRef}
role="button"
aria-describedby={isOpen ? id.current : undefined}
tabIndex={tabIndex ?? 0}
className="flex-grow-0 flex items-center"
tabIndex={tabIndex ?? -1}
className={classNames(className, 'flex-grow-0 flex items-center')}
onClick={handleToggleImmediate}
onMouseEnter={handleOpen}
onMouseLeave={handleClose}

View File

@@ -2,7 +2,7 @@ import type { HttpRequest } from '@yaakapp-internal/models';
import { formatSdl } from 'format-graphql';
import { useAtom } from 'jotai';
import { useMemo } from 'react';
import { useCallback, useMemo } from 'react';
import { useLocalStorage } from 'react-use';
import { useIntrospectGraphQL } from '../../hooks/useIntrospectGraphQL';
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
@@ -45,20 +45,31 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr
return { query: request.body.query ?? '', variables: request.body.variables ?? '' };
}, [extraEditorProps.forceUpdateKey]);
const [isDocOpenRecord, setGraphqlDocStateAtomValue] = useAtom(showGraphQLDocExplorerAtom);
const isDocOpen = isDocOpenRecord[request.id] !== undefined;
const handleChangeQuery = (query: string) => {
const newBody = { query, variables: currentBody.variables || undefined };
setCurrentBody(newBody);
onChange(newBody);
};
const handleChangeQuery = useCallback(
(query: string) => {
setCurrentBody(({ variables }) => {
const newBody = { query, variables };
onChange(newBody);
return newBody;
});
},
[onChange, setCurrentBody],
);
const handleChangeVariables = (variables: string) => {
const newBody = { query: currentBody.query, variables: variables || undefined };
setCurrentBody(newBody);
onChange(newBody);
};
const handleChangeVariables = useCallback(
(variables: string) => {
setCurrentBody(({ query }) => {
const newBody = { query, variables: variables || undefined };
onChange(newBody);
return newBody;
});
},
[onChange, setCurrentBody],
);
const actions = useMemo<EditorProps['actions']>(
() => [

View File

@@ -20,6 +20,7 @@
"@gilbarbara/deep-equal": "^0.3.1",
"@lezer/highlight": "^1.1.3",
"@lezer/lr": "^1.3.3",
"@prantlf/jsonlint": "^16.0.0",
"@replit/codemirror-emacs": "^6.1.0",
"@replit/codemirror-vim": "^6.3.0",
"@replit/codemirror-vscode-keymap": "^6.0.2",