diff --git a/.github/workflows/production.yaml b/.github/workflows/production.yaml index ab6f8cf0..adca8ecb 100644 --- a/.github/workflows/production.yaml +++ b/.github/workflows/production.yaml @@ -28,7 +28,7 @@ jobs: run: npx playwright install --with-deps - name: Run Playwright Tests - run: npx playwright test + run: NODE_OPTIONS="--no-experimental-strip-types" npx playwright test - name: Upload Playwright Traces if: failure() diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 7627c212..34a9871c 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -27,7 +27,7 @@ jobs: run: npx playwright install --with-deps - name: Run Playwright Tests - run: npx playwright test + run: NODE_OPTIONS="--no-experimental-strip-types" npx playwright test - name: Upload Playwright Traces if: failure() diff --git a/e2e/decoder.spec.ts b/e2e/decoder.spec.ts index 27963f7d..c9b367bf 100644 --- a/e2e/decoder.spec.ts +++ b/e2e/decoder.spec.ts @@ -26,7 +26,8 @@ import { } from "./e2e.utils"; import { MessageStatusValue, MessageTypeValue } from "./e2e.values"; import { JwtDictionaryModel, JwtSignedWithDigitalModel } from "./e2e.models"; -import jwts from "./jwt.json" assert { type: "json" }; +import jwts from "./jwt.json" with { type: "json" }; +import { EncodingValues } from "@/features/common/values/encoding.values"; const TestJwts = (jwts as JwtDictionaryModel).byAlgorithm; @@ -39,7 +40,7 @@ test.describe("Can interact with JWT Decoder JWT editor", () => { const jwtEditorInput = getDecoderJwtEditorInput(page); await expect(jwtEditorInput).toHaveValue( - "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30" ); }); @@ -53,8 +54,13 @@ test.describe("Can interact with JWT Decoder JWT editor", () => { await expect(jwtEditorInput).toHaveValue(inputValue); }); - test("can copy value in JWT editor", async ({ page, context, browserName }) => { - const permissions = browserName === 'firefox' ? [] : ["clipboard-read", "clipboard-write"] + test("can copy value in JWT editor", async ({ + page, + context, + browserName, + }) => { + const permissions = + browserName === "firefox" ? [] : ["clipboard-read", "clipboard-write"]; const inputValue = (TestJwts.RS512 as JwtSignedWithDigitalModel).withPemKey .jwt; await context.grantPermissions(permissions); @@ -76,7 +82,7 @@ test.describe("Can interact with JWT Decoder JWT editor", () => { await copyButton.click(); const clipboardContent = await page.evaluate(() => - navigator.clipboard.readText(), + navigator.clipboard.readText() ); expect(clipboardContent).toBe(inputValue); @@ -105,51 +111,14 @@ test.describe("Can generate JWT examples", () => { await page.goto(E2E_BASE_URL); }); - test("Can open and close JWT Decoder example widget", async ({ page }) => { - const lang = await getLang(page); - expectToBeNonNull(lang); - - const pickersUiDictionary = getPickersUiDictionary(lang); - - const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); - - const exampleButton = decoderWidget.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.label, - }); - - await exampleButton.click(); - - await expect(exampleButton).not.toBeVisible(); - - const closeButton = page.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.closeButton.label, - }); - - await closeButton.click(); - - await expect(exampleButton).toBeVisible(); - await expect(closeButton).not.toBeVisible(); - }); - test.describe("Can generate a JWT decoder example", () => { test.beforeEach(async ({ page }) => { const lang = await getLang(page); expectToBeNonNull(lang); const pickersUiDictionary = getPickersUiDictionary(lang); - - const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); - - const exampleButton = decoderWidget.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.label, - }); - - await exampleButton.click(); - - await expect(exampleButton).not.toBeVisible(); - - const pickerIndicator = decoderWidget.getByText( - pickersUiDictionary.exampleAlgPicker.defaultValue, + const pickerIndicator = page.getByText( + pickersUiDictionary.exampleAlgPicker.defaultValue ); await pickerIndicator.click(); @@ -174,7 +143,7 @@ test.describe("Can generate JWT examples", () => { const targetToken = DefaultTokensValues[option]; const jwtEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.jwtEditor.id, + dataTestidDictionary.decoder.jwtEditor.id ); const jwtEditorInput = jwtEditor.getByRole("textbox"); @@ -203,7 +172,7 @@ test.describe("Can generate JWT examples", () => { }); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); const secretKeyInput = secretKeyEditor.getByRole("textbox"); @@ -217,9 +186,13 @@ test.describe("Can generate JWT examples", () => { status: MessageStatusValue.VISIBLE, }); - const encodingValue = await secretKeyEditor - .locator(".react-select__single-value") - .innerText(); + const isEncodingSwitchChecked = await page + .getByRole("switch") + .isChecked(); + + const encodingValue = isEncodingSwitchChecked + ? EncodingValues.BASE64URL + : EncodingValues.UTF8; expect(encodingValue).toBe(symmetricToken.secretEncoding); @@ -236,8 +209,9 @@ test.describe("Can generate JWT examples", () => { status: MessageStatusValue.VISIBLE, }); - const formatValue = await secretKeyEditor + const formatValue = await page .locator(".react-select__single-value") + .nth(1) .innerText(); expect(formatValue).toBe(asymmetricToken.publicKeyFormat); @@ -270,7 +244,7 @@ test.describe("decode JWTs", () => { const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); const jwtEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.jwtEditor.id, + dataTestidDictionary.decoder.jwtEditor.id ); const jwtEditorInput = jwtEditor.getByRole("textbox"); @@ -327,14 +301,14 @@ test.describe("decode JWTs", () => { }); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); await secretKeyEditorInput.fill(entryWithUtf8Secret.secret); await expect(secretKeyEditorInput).toHaveValue( - entryWithUtf8Secret.secret, + entryWithUtf8Secret.secret ); await checkSecretKeyDecoderEditorStatusBarMessage({ @@ -343,9 +317,13 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const encodingValue = await secretKeyEditor - .locator(".react-select__single-value") - .innerText(); + const isEncodingSwitchChecked = await page + .getByRole("switch") + .isChecked(); + + const encodingValue = isEncodingSwitchChecked + ? EncodingValues.BASE64URL + : EncodingValues.UTF8; expect(encodingValue).toBe(entryWithUtf8Secret.secretEncoding); @@ -388,17 +366,13 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", - ); - - await formatPicker.click(); + const encodingSwitch = await page.getByTestId(dataTestidDictionary.encoder.switch) + expect(encodingSwitch).toBeVisible() - await page - .getByRole("option", { - name: entryWithBase64urlSecret.secretEncoding, - }) - .click(); + if ( + entryWithBase64urlSecret.secretEncoding === EncodingValues.BASE64URL + ) + await encodingSwitch.click(); await secretKeyEditorInput.fill(entryWithBase64urlSecret.secret); @@ -451,7 +425,7 @@ test.describe("decode JWTs", () => { }); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); @@ -465,7 +439,7 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const formatValue = await secretKeyEditor + const formatValue = await page .locator(".react-select__single-value") .innerText(); @@ -511,8 +485,8 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", + const formatPicker = page.locator( + ".react-select__single-value" ); await formatPicker.click(); @@ -530,7 +504,7 @@ test.describe("decode JWTs", () => { }); await secretKeyEditorInput.fill( - JSON.stringify(entrywithJwkKey.publicKey, null, 2), + JSON.stringify(entrywithJwkKey.publicKey, null, 2) ); await checkJwtEditorStatusBarMessage({ @@ -581,8 +555,8 @@ test.describe("decode JWTs", () => { status: MessageStatusValue.VISIBLE, }); - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", + const formatPicker = page.locator( + ".react-select__single-value" ); await formatPicker.click(); @@ -594,7 +568,7 @@ test.describe("decode JWTs", () => { .click(); await secretKeyEditorInput.fill( - JSON.stringify(entrywithJwkKey.publicKey, null, 2), + JSON.stringify(entrywithJwkKey.publicKey, null, 2) ); await checkJwtEditorStatusBarMessage({ @@ -642,10 +616,10 @@ test("Can decode JWTs signed with a non-supported algorithm", async ({ kid: "sig", }; const expectedDecodedPayload = { - "sub": "1234567890", - "name": "John Doe", - "admin": true, - "iat": 1516239022 + sub: "1234567890", + name: "John Doe", + admin: true, + iat: 1516239022, }; await page.goto(E2E_BASE_URL); @@ -656,7 +630,7 @@ test("Can decode JWTs signed with a non-supported algorithm", async ({ const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); const jwtEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.jwtEditor.id, + dataTestidDictionary.decoder.jwtEditor.id ); const jwtEditorInput = jwtEditor.getByRole("textbox"); @@ -678,22 +652,22 @@ test("Can decode JWTs signed with a non-supported algorithm", async ({ await expect( decoderWidget.getByTestId( - dataTestidDictionary.decoder.decodedHeader.json.id, - ), + dataTestidDictionary.decoder.decodedHeader.json.id + ) ).toHaveText(JSON.stringify(expectedDecodedHeader, null, 2), { useInnerText: true, }); await expect( decoderWidget.getByTestId( - dataTestidDictionary.decoder.decodedPayload.json.id, - ), + dataTestidDictionary.decoder.decodedPayload.json.id + ) ).toHaveText(JSON.stringify(expectedDecodedPayload, null, 2), { useInnerText: true, }); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); await expect(secretKeyEditor).not.toBeVisible(); @@ -711,10 +685,10 @@ test.describe("Decode pieces of JWTs in Base64Url", () => { jwtPiece: "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0", expectedDecodedOutput: { - "sub": "1234567890", - "name": "John Doe", - "admin": true, - "iat": 1516239022 + sub: "1234567890", + name: "John Doe", + admin: true, + iat: 1516239022, }, }, { @@ -737,7 +711,7 @@ test.describe("Decode pieces of JWTs in Base64Url", () => { const decoderWidget = page.getByTestId(dataTestidDictionary.decoder.id); const jwtEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.jwtEditor.id, + dataTestidDictionary.decoder.jwtEditor.id ); const jwtEditorInput = jwtEditor.getByRole("textbox"); @@ -759,20 +733,20 @@ test.describe("Decode pieces of JWTs in Base64Url", () => { await expect( decoderWidget.getByTestId( - dataTestidDictionary.decoder.decodedHeader.json.id, - ), + dataTestidDictionary.decoder.decodedHeader.json.id + ) ).toHaveText(JSON.stringify(expectedDecodedOutput, null, 2), { useInnerText: true, }); await expect( decoderWidget.getByTestId( - dataTestidDictionary.decoder.decodedPayload.json.id, - ), + dataTestidDictionary.decoder.decodedPayload.json.id + ) ).toBeEmpty(); const secretKeyEditor = decoderWidget.getByTestId( - dataTestidDictionary.decoder.secretKeyEditor.id, + dataTestidDictionary.decoder.secretKeyEditor.id ); await expect(secretKeyEditor).not.toBeVisible(); diff --git a/e2e/encoder.spec.ts b/e2e/encoder.spec.ts index 5cf26eec..777ddad1 100644 --- a/e2e/encoder.spec.ts +++ b/e2e/encoder.spec.ts @@ -15,7 +15,7 @@ import { getLang, switchToEncoderTab, } from "./e2e.utils"; -import jwts from "./jwt.json" assert { type: "json" }; +import jwts from "./jwt.json" with { type: "json" }; import { JwtDictionaryModel, JwtSignedWithDigitalModel, @@ -27,6 +27,7 @@ import { isNoneAlg, } from "@/features/common/services/jwt.service"; import { MessageStatusValue, MessageTypeValue } from "./e2e.values"; +import { EncodingValues } from "@/features/common/values/encoding.values"; const TestJwts = (jwts as JwtDictionaryModel).byAlgorithm; @@ -41,7 +42,7 @@ test.describe("Can interact with header editor in JWT Encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const headerEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.headerEditor.id, + dataTestidDictionary.encoder.headerEditor.id ); const headerEditorInput = headerEditor.getByRole("textbox"); @@ -65,7 +66,7 @@ test.describe("Can interact with header editor in JWT Encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const headerEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.headerEditor.id, + dataTestidDictionary.encoder.headerEditor.id ); const headerEditorInput = headerEditor.getByRole("textbox"); @@ -83,7 +84,7 @@ test.describe("Can interact with header editor in JWT Encoder", () => { const encoder = page.getByTestId(dataTestidDictionary.encoder.id); const headerEditor = encoder.getByTestId( - dataTestidDictionary.encoder.headerEditor.id, + dataTestidDictionary.encoder.headerEditor.id ); const headerEditorInput = headerEditor.getByRole("textbox"); @@ -107,7 +108,7 @@ test.describe("can interact with payload editor in JWT encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const payloadEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.payloadEditor.id, + dataTestidDictionary.encoder.payloadEditor.id ); const payloadEditorInput = payloadEditor.getByRole("textbox"); @@ -131,7 +132,7 @@ test.describe("can interact with payload editor in JWT encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const payloadEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.payloadEditor.id, + dataTestidDictionary.encoder.payloadEditor.id ); const payloadEditorInput = payloadEditor.getByRole("textbox"); @@ -149,7 +150,7 @@ test.describe("can interact with payload editor in JWT encoder", () => { const encoder = page.getByTestId(dataTestidDictionary.encoder.id); const payloadEditor = encoder.getByTestId( - dataTestidDictionary.encoder.payloadEditor.id, + dataTestidDictionary.encoder.payloadEditor.id ); const payloadEditorInput = payloadEditor.getByRole("textbox"); @@ -173,7 +174,7 @@ test.describe("can interact with secret editor in JWT encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const secretKeyEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.secretKeyEditor.id, + dataTestidDictionary.encoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); @@ -191,7 +192,7 @@ test.describe("can interact with secret editor in JWT encoder", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const secretKeyEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.secretKeyEditor.id, + dataTestidDictionary.encoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); @@ -209,7 +210,7 @@ test.describe("can interact with secret editor in JWT encoder", () => { const encoder = page.getByTestId(dataTestidDictionary.encoder.id); const secretKeyEditor = encoder.getByTestId( - dataTestidDictionary.encoder.secretKeyEditor.id, + dataTestidDictionary.encoder.secretKeyEditor.id ); const secretKeyEditorInput = secretKeyEditor.getByRole("textbox"); @@ -229,32 +230,6 @@ test.describe("Generate JWT encoding examples", () => { await switchToEncoderTab(page); }); - test("Can open and close encoder example widget", async ({ page }) => { - const lang = await getLang(page); - expectToBeNonNull(lang); - - const pickersUiDictionary = getPickersUiDictionary(lang); - - const encoder = page.getByTestId(dataTestidDictionary.encoder.id); - - const exampleButton = encoder.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.label, - }); - - await exampleButton.click(); - - await expect(exampleButton).not.toBeVisible(); - - const closeButton = page.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.closeButton.label, - }); - - await closeButton.click(); - - await expect(exampleButton).toBeVisible(); - await expect(closeButton).not.toBeVisible(); - }); - test.describe("Can generate a JWT example", () => { test.beforeEach(async ({ page }) => { const lang = await getLang(page); @@ -262,18 +237,8 @@ test.describe("Generate JWT encoding examples", () => { const pickersUiDictionary = getPickersUiDictionary(lang); - const encoder = page.getByTestId(dataTestidDictionary.encoder.id); - - const exampleButton = encoder.getByRole("button", { - name: pickersUiDictionary.exampleAlgPicker.label, - }); - - await exampleButton.click(); - - await expect(exampleButton).not.toBeVisible(); - - const pickerIndicator = encoder.getByText( - pickersUiDictionary.exampleAlgPicker.defaultValue, + const pickerIndicator = page.getByText( + pickersUiDictionary.exampleAlgPicker.defaultValue ); await pickerIndicator.click(); @@ -311,7 +276,7 @@ test.describe("Generate JWT encoding examples", () => { } await expect(jwtOutput).toHaveValue( - /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/, + /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/ ); }); }); @@ -336,13 +301,13 @@ test.describe("encode JWTs", () => { const encoderWidget = page.getByTestId(dataTestidDictionary.encoder.id); const headerEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.headerEditor.id, + dataTestidDictionary.encoder.headerEditor.id ); const payloadEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.payloadEditor.id, + dataTestidDictionary.encoder.payloadEditor.id ); const secretKeyEditor = encoderWidget.getByTestId( - dataTestidDictionary.encoder.secretKeyEditor.id, + dataTestidDictionary.encoder.secretKeyEditor.id ); const jwtOutput = encoderWidget .getByTestId(dataTestidDictionary.encoder.jwt.id) @@ -415,16 +380,13 @@ test.describe("encode JWTs", () => { await expect(jwtOutput).toHaveValue(tokenWithUtf8Secret.jwt); - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", - ); - await formatPicker.click(); - - await page - .getByRole("option", { - name: tokenWithBase64urlSecret.secretEncoding, - }) - .click(); + const encodingSwitch = await page.getByTestId(dataTestidDictionary.encoder.switch) + expect(encodingSwitch).toBeVisible() + + if ( + tokenWithBase64urlSecret.secretEncoding === EncodingValues.BASE64URL + ) + await encodingSwitch.click(); await checkSecretKeyEncoderEditorStatusBarMessage({ page, @@ -479,12 +441,12 @@ test.describe("encode JWTs", () => { await expect(jwtOutput).toHaveValue(tokenWithPemKey.jwt); } else { await expect(jwtOutput).toHaveValue( - /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/, + /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/ ); } - const formatPicker = secretKeyEditor.locator( - ".react-select__single-value", + const formatPicker = page.locator( + ".react-select__single-value" ); await formatPicker.click(); @@ -501,7 +463,7 @@ test.describe("encode JWTs", () => { }); await secretKeyEditorInput.fill( - JSON.stringify(tokenWithJwkKey.privateKey, null, 2), + JSON.stringify(tokenWithJwkKey.privateKey, null, 2) ); await checkSecretKeyEncoderEditorStatusBarMessage({ @@ -514,7 +476,7 @@ test.describe("encode JWTs", () => { await expect(jwtOutput).toHaveValue(tokenWithJwkKey.jwt); } else { await expect(jwtOutput).toHaveValue( - /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/, + /^[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*$/ ); } diff --git a/next.config.mjs b/next.config.mjs index f4672921..8421cd46 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -3,18 +3,26 @@ import createMDX from "@next/mdx"; /** @type {import('next').NextConfig} */ const nextConfig = { webpack(config) { - config.module.rules.push({ - test: /\.svg$/, - use: { - loader: "@svgr/webpack", - options: { - svgoConfig: { - plugins: ["prefixIds"], + config.module.rules.push( + { + test: /\.svg$/, + resourceQuery: { not: /raw/ }, + use: { + loader: "@svgr/webpack", + options: { + svgoConfig: { + plugins: ["prefixIds"], + }, + ref: true, }, - ref: true, }, }, - }); + { + test: /\.svg$/i, + resourceQuery: /raw/, // Only apply this rule if '?raw' is present + type: "asset/source", + } + ); return config; }, diff --git a/package-lock.json b/package-lock.json index f6f42523..cc15e73a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,7 +29,7 @@ "nanoid": "^5.0.9", "negotiator": "^1.0.0", "neverthrow": "^8.1.1", - "next": "^14.2.25", + "next": "14.2.35", "node-forge": "^1.3.1", "octokit": "^4.0.2", "prismjs": "^1.29.0", @@ -109,6 +109,7 @@ "version": "7.24.4", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.4.tgz", "integrity": "sha512-MBVlMXP+kkl5394RBLSxxk/iLTeVGuXTV3cIDXavPpMMqnSnt6apKgan/U8O3USWZCWZT/TbgfEpKa4uMgN4Dg==", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.2", @@ -2636,6 +2637,7 @@ "resolved": "https://registry.npmjs.org/@mdx-js/loader/-/loader-3.1.0.tgz", "integrity": "sha512-xU/lwKdOyfXtQGqn3VnJjlDrmKXEvMi1mgYxVmukEUtVycIz1nh7oQ40bKTd4cA7rLStqu0740pnhGYxGoqsCg==", "license": "MIT", + "peer": true, "dependencies": { "@mdx-js/mdx": "^3.0.0", "source-map": "^0.7.0" @@ -2694,6 +2696,7 @@ "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/mdx": "^2.0.0" }, @@ -2707,9 +2710,9 @@ } }, "node_modules/@next/env": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.29.tgz", - "integrity": "sha512-UzgLR2eBfhKIQt0aJ7PWH7XRPYw7SXz0Fpzdl5THjUnvxy4kfBk9OU4RNPNiETewEEtaBcExNFNn1QWH8wQTjg==", + "version": "14.2.35", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.35.tgz", + "integrity": "sha512-DuhvCtj4t9Gwrx80dmz2F4t/zKQ4ktN8WrMwOuVzkJfBilwAwGr6v16M5eI8yCuZ63H9TTuEU09Iu2HqkzFPVQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -2743,9 +2746,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.29.tgz", - "integrity": "sha512-wWtrAaxCVMejxPHFb1SK/PVV1WDIrXGs9ki0C/kUM8ubKHQm+3hU9MouUywCw8Wbhj3pewfHT2wjunLEr/TaLA==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.33.tgz", + "integrity": "sha512-HqYnb6pxlsshoSTubdXKu15g3iivcbsMXg4bYpjL2iS/V6aQot+iyF4BUc2qA/J/n55YtvE4PHMKWBKGCF/+wA==", "cpu": [ "arm64" ], @@ -2759,9 +2762,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.29.tgz", - "integrity": "sha512-7Z/jk+6EVBj4pNLw/JQrvZVrAh9Bv8q81zCFSfvTMZ51WySyEHWVpwCEaJY910LyBftv2F37kuDPQm0w9CEXyg==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.33.tgz", + "integrity": "sha512-8HGBeAE5rX3jzKvF593XTTFg3gxeU4f+UWnswa6JPhzaR6+zblO5+fjltJWIZc4aUalqTclvN2QtTC37LxvZAA==", "cpu": [ "x64" ], @@ -2775,9 +2778,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.29.tgz", - "integrity": "sha512-o6hrz5xRBwi+G7JFTHc+RUsXo2lVXEfwh4/qsuWBMQq6aut+0w98WEnoNwAwt7hkEqegzvazf81dNiwo7KjITw==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.33.tgz", + "integrity": "sha512-JXMBka6lNNmqbkvcTtaX8Gu5by9547bukHQvPoLe9VRBx1gHwzf5tdt4AaezW85HAB3pikcvyqBToRTDA4DeLw==", "cpu": [ "arm64" ], @@ -2791,9 +2794,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.29.tgz", - "integrity": "sha512-9i+JEHBOVgqxQ92HHRFlSW1EQXqa/89IVjtHgOqsShCcB/ZBjTtkWGi+SGCJaYyWkr/lzu51NTMCfKuBf7ULNw==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.33.tgz", + "integrity": "sha512-Bm+QulsAItD/x6Ih8wGIMfRJy4G73tu1HJsrccPW6AfqdZd0Sfm5Imhgkgq2+kly065rYMnCOxTBvmvFY1BKfg==", "cpu": [ "arm64" ], @@ -2807,9 +2810,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.29.tgz", - "integrity": "sha512-B7JtMbkUwHijrGBOhgSQu2ncbCYq9E7PZ7MX58kxheiEOwdkM+jGx0cBb+rN5AeqF96JypEppK6i/bEL9T13lA==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.33.tgz", + "integrity": "sha512-FnFn+ZBgsVMbGDsTqo8zsnRzydvsGV8vfiWwUo1LD8FTmPTdV+otGSWKc4LJec0oSexFnCYVO4hX8P8qQKaSlg==", "cpu": [ "x64" ], @@ -2823,9 +2826,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.29.tgz", - "integrity": "sha512-yCcZo1OrO3aQ38B5zctqKU1Z3klOohIxug6qdiKO3Q3qNye/1n6XIs01YJ+Uf+TdpZQ0fNrOQI2HrTLF3Zprnw==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.33.tgz", + "integrity": "sha512-345tsIWMzoXaQndUTDv1qypDRiebFxGYx9pYkhwY4hBRaOLt8UGfiWKr9FSSHs25dFIf8ZqIFaPdy5MljdoawA==", "cpu": [ "x64" ], @@ -2839,9 +2842,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.29.tgz", - "integrity": "sha512-WnrfeOEtTVidI9Z6jDLy+gxrpDcEJtZva54LYC0bSKQqmyuHzl0ego+v0F/v2aXq0am67BRqo/ybmmt45Tzo4A==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.33.tgz", + "integrity": "sha512-nscpt0G6UCTkrT2ppnJnFsYbPDQwmum4GNXYTeoTIdsmMydSKFz9Iny2jpaRupTb+Wl298+Rh82WKzt9LCcqSQ==", "cpu": [ "arm64" ], @@ -2855,9 +2858,9 @@ } }, "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.29.tgz", - "integrity": "sha512-vkcriFROT4wsTdSeIzbxaZjTNTFKjSYmLd8q/GVH3Dn8JmYjUKOuKXHK8n+lovW/kdcpIvydO5GtN+It2CvKWA==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz", + "integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==", "cpu": [ "ia32" ], @@ -2871,9 +2874,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.29.tgz", - "integrity": "sha512-iPPwUEKnVs7pwR0EBLJlwxLD7TTHWS/AoVZx1l9ZQzfQciqaFEr5AlYzA2uB6Fyby1IF18t4PL0nTpB+k4Tzlw==", + "version": "14.2.33", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.33.tgz", + "integrity": "sha512-nOjfZMy8B94MdisuzZo9/57xuFVLHJaDj5e/xrduJp9CV2/HrfxTRH2fbyLe+K9QT41WBLUd4iXX3R7jBp0EUg==", "cpu": [ "x64" ], @@ -3456,7 +3459,6 @@ "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 18" } @@ -3524,7 +3526,6 @@ "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", "license": "MIT", - "peer": true, "dependencies": { "@octokit/types": "^13.1.0", "universal-user-agent": "^6.0.0" @@ -3538,7 +3539,6 @@ "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.0.tgz", "integrity": "sha512-r+oZUH7aMFui1ypZnAvZmn0KSqAUgE1/tUXIWaqUCa1758ts/Jio84GZuzsvUkme98kv0WFY8//n0J1Z+vsIsQ==", "license": "MIT", - "peer": true, "dependencies": { "@octokit/request": "^8.3.0", "@octokit/types": "^13.0.0", @@ -3833,7 +3833,6 @@ "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", "license": "MIT", - "peer": true, "dependencies": { "@octokit/endpoint": "^9.0.6", "@octokit/request-error": "^5.1.1", @@ -3849,7 +3848,6 @@ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.0.tgz", "integrity": "sha512-GETXfE05J0+7H2STzekpKObFe765O5dlAKUTLNGeH+x47z7JjXHfsHKo5z21D/o/IOZTUEI6nyWyR+bZVP/n5Q==", "license": "MIT", - "peer": true, "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", @@ -3864,7 +3862,6 @@ "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", "license": "MIT", - "peer": true, "dependencies": { "@octokit/types": "^13.1.0", "deprecation": "^2.0.0", @@ -3947,6 +3944,7 @@ "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "playwright": "1.49.1" }, @@ -6123,6 +6121,7 @@ "resolved": "https://registry.npmjs.org/@svgr/core/-/core-8.1.0.tgz", "integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==", "license": "MIT", + "peer": true, "dependencies": { "@babel/core": "^7.21.3", "@svgr/babel-preset": "8.1.0", @@ -6363,6 +6362,7 @@ "version": "18.2.73", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.73.tgz", "integrity": "sha512-XcGdod0Jjv84HOC7N5ziY3x+qL0AfmubvKOZ9hJjJ2yd5EE+KYjWhdOjt387e9HPheHkdggF9atTifMRtyAaRA==", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -6692,6 +6692,7 @@ "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -7129,8 +7130,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/binary-extensions": { "version": "2.3.0", @@ -7200,6 +7200,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001669", "electron-to-chromium": "^1.5.41", @@ -7805,8 +7806,7 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/dequal": { "version": "2.0.3", @@ -8253,6 +8253,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -8405,6 +8406,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz", "integrity": "sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==", "dev": true, + "peer": true, "dependencies": { "array-includes": "^3.1.7", "array.prototype.findlastindex": "^1.2.3", @@ -11318,12 +11320,13 @@ } }, "node_modules/next": { - "version": "14.2.29", - "resolved": "https://registry.npmjs.org/next/-/next-14.2.29.tgz", - "integrity": "sha512-s98mCOMOWLGGpGOfgKSnleXLuegvvH415qtRZXpSp00HeEgdmrxmwL9cgKU+h4XrhB16zEI5d/7BnkS3ATInsA==", + "version": "14.2.35", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.35.tgz", + "integrity": "sha512-KhYd2Hjt/O1/1aZVX3dCwGXM1QmOV4eNM2UTacK5gipDdPN/oHHK/4oVGy7X8GMfPMsUTUEmGlsy0EY1YGAkig==", "license": "MIT", + "peer": true, "dependencies": { - "@next/env": "14.2.29", + "@next/env": "14.2.35", "@swc/helpers": "0.5.5", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", @@ -11338,15 +11341,15 @@ "node": ">=18.17.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.2.29", - "@next/swc-darwin-x64": "14.2.29", - "@next/swc-linux-arm64-gnu": "14.2.29", - "@next/swc-linux-arm64-musl": "14.2.29", - "@next/swc-linux-x64-gnu": "14.2.29", - "@next/swc-linux-x64-musl": "14.2.29", - "@next/swc-win32-arm64-msvc": "14.2.29", - "@next/swc-win32-ia32-msvc": "14.2.29", - "@next/swc-win32-x64-msvc": "14.2.29" + "@next/swc-darwin-arm64": "14.2.33", + "@next/swc-darwin-x64": "14.2.33", + "@next/swc-linux-arm64-gnu": "14.2.33", + "@next/swc-linux-arm64-musl": "14.2.33", + "@next/swc-linux-x64-gnu": "14.2.33", + "@next/swc-linux-x64-musl": "14.2.33", + "@next/swc-win32-arm64-msvc": "14.2.33", + "@next/swc-win32-ia32-msvc": "14.2.33", + "@next/swc-win32-x64-msvc": "14.2.33" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", @@ -11594,6 +11597,7 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.2.tgz", "integrity": "sha512-hEb7Ma4cGJGEUNOAVmyfdB/3WirWMg5hDuNFVejGEDFqupeOysLc2sG6HJxY2etBp5YQu5Wtxwi020jS9xlUwg==", + "peer": true, "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.0.0", @@ -12210,6 +12214,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -12318,6 +12323,7 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -12856,6 +12862,7 @@ "version": "1.74.1", "resolved": "https://registry.npmjs.org/sass/-/sass-1.74.1.tgz", "integrity": "sha512-w0Z9p/rWZWelb88ISOLyvqTWGmtmu2QJICqDBGyNnfG4OUnPX9BBjjYIXUpXCMOOg5MQWNpqzt876la1fsTvUA==", + "peer": true, "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -14081,6 +14088,7 @@ "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14272,8 +14280,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/update-browserslist-db": { "version": "1.1.1", @@ -14382,6 +14389,7 @@ "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -14532,6 +14540,7 @@ "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@vitest/expect": "1.6.1", "@vitest/runner": "1.6.1", diff --git a/package.json b/package.json index 3ba940b8..4f8b87de 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "lint": "next lint", "test": "vitest", "coverage": "vitest run --coverage", - "playwright:test-ui": "npx playwright test --ui" + "playwright:test-ui": "NODE_OPTIONS=\"--no-experimental-strip-types\" npx playwright test --ui" }, "dependencies": { "@formatjs/intl-localematcher": "^0.5.7", @@ -35,7 +35,7 @@ "nanoid": "^5.0.9", "negotiator": "^1.0.0", "neverthrow": "^8.1.1", - "next": "^14.2.25", + "next": "14.2.35", "node-forge": "^1.3.1", "octokit": "^4.0.2", "prismjs": "^1.29.0", diff --git a/src/libs/theme/fonts/NotoSansJP-Bold.otf b/public/fonts/NotoSansJP-Bold.otf similarity index 100% rename from src/libs/theme/fonts/NotoSansJP-Bold.otf rename to public/fonts/NotoSansJP-Bold.otf diff --git a/src/libs/theme/fonts/NotoSansJP-Medium.otf b/public/fonts/NotoSansJP-Medium.otf similarity index 100% rename from src/libs/theme/fonts/NotoSansJP-Medium.otf rename to public/fonts/NotoSansJP-Medium.otf diff --git a/src/libs/theme/fonts/NotoSansJP-Regular.otf b/public/fonts/NotoSansJP-Regular.otf similarity index 100% rename from src/libs/theme/fonts/NotoSansJP-Regular.otf rename to public/fonts/NotoSansJP-Regular.otf diff --git a/public/fonts/japanese-fonts.css b/public/fonts/japanese-fonts.css new file mode 100644 index 00000000..63fe42f4 --- /dev/null +++ b/public/fonts/japanese-fonts.css @@ -0,0 +1,31 @@ +@font-face { + font-family: "Noto Sans JP"; + src: url("/fonts/NotoSansJP-Regular.otf") format("opentype"); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Noto Sans JP"; + src: url("/fonts/NotoSansJP-Medium.otf") format("opentype"); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: "Noto Sans JP"; + src: url("/fonts/NotoSansJP-Bold.otf") format("opentype"); + font-weight: 600; + font-style: normal; + font-display: swap; +} + +.japanese-font { + font-family: "Noto Sans JP", sans-serif; +} + +:root { + --font-japanese: "Noto Sans JP", sans-serif; +} diff --git a/public/images/ebook-image.png b/public/images/ebook-image.png deleted file mode 100644 index c24a572a..00000000 Binary files a/public/images/ebook-image.png and /dev/null differ diff --git a/public/images/ebook-old.svg b/public/images/ebook-old.svg new file mode 100644 index 00000000..05ada022 --- /dev/null +++ b/public/images/ebook-old.svg @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/ebook.svg b/public/images/ebook.svg new file mode 100644 index 00000000..dc2ee3ff --- /dev/null +++ b/public/images/ebook.svg @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/images/en.dark.auth0-flow.png b/public/images/en.dark.auth0-flow.png deleted file mode 100644 index 5c7ae1ad..00000000 Binary files a/public/images/en.dark.auth0-flow.png and /dev/null differ diff --git a/public/images/en.diagram.jpg b/public/images/en.diagram.jpg new file mode 100644 index 00000000..d87e303c Binary files /dev/null and b/public/images/en.diagram.jpg differ diff --git a/public/images/en.diagram.png b/public/images/en.diagram.png new file mode 100644 index 00000000..5ed13e24 Binary files /dev/null and b/public/images/en.diagram.png differ diff --git a/public/images/en.light.auth0-flow.png b/public/images/en.light.auth0-flow.png deleted file mode 100644 index 0e07d32c..00000000 Binary files a/public/images/en.light.auth0-flow.png and /dev/null differ diff --git a/public/images/ja.dark.auth0-flow.png b/public/images/ja.dark.auth0-flow.png deleted file mode 100644 index 3faed616..00000000 Binary files a/public/images/ja.dark.auth0-flow.png and /dev/null differ diff --git a/public/images/ja.diagram.jpg b/public/images/ja.diagram.jpg new file mode 100644 index 00000000..0437a664 Binary files /dev/null and b/public/images/ja.diagram.jpg differ diff --git a/public/images/ja.diagram.png b/public/images/ja.diagram.png new file mode 100644 index 00000000..5815c1e2 Binary files /dev/null and b/public/images/ja.diagram.png differ diff --git a/public/images/ja.light.auth0-flow.png b/public/images/ja.light.auth0-flow.png deleted file mode 100644 index 39b6d112..00000000 Binary files a/public/images/ja.light.auth0-flow.png and /dev/null differ diff --git a/public/images/mobile.dark.auth0-flow-bg.svg b/public/images/mobile.dark.auth0-flow-bg.svg deleted file mode 100644 index 8f861605..00000000 --- a/public/images/mobile.dark.auth0-flow-bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.en.dark.auth0-flow.svg b/public/images/mobile.en.dark.auth0-flow.svg deleted file mode 100644 index bed98685..00000000 --- a/public/images/mobile.en.dark.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.en.light.auth0-flow.svg b/public/images/mobile.en.light.auth0-flow.svg deleted file mode 100644 index 9a15f08e..00000000 --- a/public/images/mobile.en.light.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.ja.dark.auth0-flow.svg b/public/images/mobile.ja.dark.auth0-flow.svg deleted file mode 100644 index 88adfb6e..00000000 --- a/public/images/mobile.ja.dark.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.ja.light.auth0-flow.svg b/public/images/mobile.ja.light.auth0-flow.svg deleted file mode 100644 index 9f9f0d12..00000000 --- a/public/images/mobile.ja.light.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/mobile.light.auth0-flow-bg.svg b/public/images/mobile.light.auth0-flow-bg.svg deleted file mode 100644 index 2701f5dd..00000000 --- a/public/images/mobile.light.auth0-flow-bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.dark.auth0-flow-bg.svg b/public/images/tablet.dark.auth0-flow-bg.svg deleted file mode 100644 index 86b3ae54..00000000 --- a/public/images/tablet.dark.auth0-flow-bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.en.dark.auth0-flow.svg b/public/images/tablet.en.dark.auth0-flow.svg deleted file mode 100644 index 03d06c6d..00000000 --- a/public/images/tablet.en.dark.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.en.light.auth0-flow.svg b/public/images/tablet.en.light.auth0-flow.svg deleted file mode 100644 index c0854376..00000000 --- a/public/images/tablet.en.light.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.ja.dark.auth0-flow.svg b/public/images/tablet.ja.dark.auth0-flow.svg deleted file mode 100644 index fc01b4c1..00000000 --- a/public/images/tablet.ja.dark.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.ja.light.auth0-flow.svg b/public/images/tablet.ja.light.auth0-flow.svg deleted file mode 100644 index 5c9855e2..00000000 --- a/public/images/tablet.ja.light.auth0-flow.svg +++ /dev/null @@ -1,113 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/images/tablet.light.auth0-flow-bg.svg b/public/images/tablet.light.auth0-flow-bg.svg deleted file mode 100644 index 9e875a45..00000000 --- a/public/images/tablet.light.auth0-flow-bg.svg +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/public/img/jwt-logo.svg b/public/img/jwt-logo.svg new file mode 100644 index 00000000..54d020e9 --- /dev/null +++ b/public/img/jwt-logo.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/jwt-symbol.svg b/public/img/jwt-symbol.svg new file mode 100644 index 00000000..63da72ae --- /dev/null +++ b/public/img/jwt-symbol.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/img/jwt-wordmark.svg b/public/img/jwt-wordmark.svg new file mode 100644 index 00000000..3a2ecbd0 --- /dev/null +++ b/public/img/jwt-wordmark.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/public/llms.txt b/public/llms.txt new file mode 100644 index 00000000..8c46daea --- /dev/null +++ b/public/llms.txt @@ -0,0 +1,42 @@ +# jwt.io + +> JWT.IO allows you to decode, verify, and generate JSON Web Tokens. + +## About + +jwt.io is the go-to resource for JSON Web Tokens (JWTs). It provides: +- An interactive JWT debugger to decode and verify tokens +- A comprehensive library directory for JWT implementations +- Educational content explaining JWT concepts + +Maintained by Auth0. + +## Quick Links + +- Debugger: https://jwt.io +- Libraries: https://jwt.io/libraries +- Introduction: https://jwt.io/introduction + +## JWT Overview + +A JSON Web Token consists of three Base64Url-encoded parts: +1. Header - algorithm and token type +2. Payload - claims (registered, public, private) +3. Signature - ensures token integrity + +Format: xxxxx.yyyyy.zzzzz + +## For AI Agents + +Claude Code users can install JWT skills for token operations: +- `/jwt-decode` - Decode and inspect JWTs +- `/jwt-encode` - Create and sign JWTs +- `/jwt-validate` - Verify JWT signatures and claims + +Install: `npx skills add jsonwebtoken/jwt-skills` +Repository: https://github.com/jsonwebtoken/jwt-skills + +## Resources + +- RFC 7519: https://datatracker.ietf.org/doc/html/rfc7519 +- Source: https://github.com/jsonwebtoken/jsonwebtoken.github.io diff --git a/src/app/[language]/page.tsx b/src/app/[language]/page.tsx index 4793d8e6..ef6a1637 100644 --- a/src/app/[language]/page.tsx +++ b/src/app/[language]/page.tsx @@ -16,12 +16,6 @@ import { DECODED_PAYLOAD_DESCRIPTION_KEY, DECODED_PAYLOAD_FORMAT_KEY, } from "@/features/decoder/config/decoder.config"; -import { - JWT_INFO_STATE_KEY, - JWT_WARNING_STATE_KEY, -} from "@/features/home/config/home.config"; -import { HeroModalStateValues } from "@/features/home/values/hero-modal-state.values"; -import { getSanitizedHeroModalStateValue } from "@/features/common/services/hero.utils"; import { getSanitizedDescriptionVisibilityValue } from "@/features/common/services/decoder.utils"; import { ClaimDescriptionVisibilityValues } from "@/features/common/values/claim-description-visibility.values"; import { getSanitizedDebuggerModeValues } from "@/features/common/services/debugger.utils"; @@ -60,15 +54,6 @@ export default function Home({ cookies().get(DECODED_PAYLOAD_DESCRIPTION_KEY)?.value || null, ) || ClaimDescriptionVisibilityValues.VISIBLE; - const jwtInfoState = - getSanitizedHeroModalStateValue( - cookies().get(JWT_INFO_STATE_KEY)?.value || null, - ) || HeroModalStateValues.OPEN; - const jwtWarningState = - getSanitizedHeroModalStateValue( - cookies().get(JWT_WARNING_STATE_KEY)?.value || null, - ) || HeroModalStateValues.OPEN; - const debuggerInitialMode: DebuggerModeValues = getSanitizedDebuggerModeValues( cookies().get(DEBUGGER_MODE_KEY)?.value || null, @@ -81,8 +66,6 @@ export default function Home({ decodedPayloadInitialTabId={decodedPayloadInitialTabId} decodedHeaderDescriptionVisibility={decodedHeaderDescriptionVisibility} decodedPayloadDescriptionVisibility={decodedPayloadDescriptionVisibility} - jwtInfoState={jwtInfoState} - jwtWarningState={jwtWarningState} // debuggerInitialMode={debuggerInitialMode} /> ); diff --git a/src/app/apple-icon.tsx b/src/app/apple-icon.tsx index f594f156..85294345 100644 --- a/src/app/apple-icon.tsx +++ b/src/app/apple-icon.tsx @@ -86,62 +86,62 @@ export default function Icon({ id }: { id: string }) { xmlns="http://www.w3.org/2000/svg" > diff --git a/src/app/global-error.tsx b/src/app/global-error.tsx index 911ceda8..6977c3da 100644 --- a/src/app/global-error.tsx +++ b/src/app/global-error.tsx @@ -22,7 +22,6 @@ export default function GlobalError({ diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index 2cdb2033..84c939b1 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -15,7 +15,6 @@ export default function NotFound() { diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index fcce47f3..2783b8c7 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -6,14 +6,35 @@ export default function sitemap(): MetadataRoute.Sitemap { { url: BASE_URL, lastModified: new Date(), + alternates: { + languages: { + en: `${BASE_URL}/`, + ja: `${BASE_URL}/ja/`, + "x-default": `${BASE_URL}/`, + }, + }, }, { url: `${BASE_URL}/introduction`, lastModified: new Date(), + alternates: { + languages: { + en: `${BASE_URL}/introduction`, + ja: `${BASE_URL}/ja/introduction`, + "x-default": `${BASE_URL}/introduction`, + }, + }, }, { url: `${BASE_URL}/libraries`, lastModified: new Date(), + alternates: { + languages: { + en: `${BASE_URL}/libraries`, + ja: `${BASE_URL}/ja/libraries`, + "x-default": `${BASE_URL}/libraries`, + }, + }, }, ]; } diff --git a/src/data/libraries-next.json b/src/data/libraries-next.json index b955dd18..cac1c338 100644 --- a/src/data/libraries-next.json +++ b/src/data/libraries-next.json @@ -848,6 +848,7 @@ "nbf": true, "iat": true, "jti": true, + "typ": true, "hs256": true, "hs384": true, "hs512": true, @@ -855,11 +856,13 @@ "rs384": true, "rs512": true, "es256": true, + "es256k": true, "es384": true, "es512": true, - "ps256": false, - "ps384": false, - "ps512": false + "ps256": true, + "ps384": true, + "ps512": true, + "eddsa": true }, "authorUrl": "https://github.com/greyblake", "authorName": "Sergey Potapov", @@ -868,7 +871,7 @@ "installCommandMarkdown": [ "git clone https://github.com/crystal-community/jwt.git" ], - "stars": 205 + "stars": 211 } ] }, @@ -2435,34 +2438,37 @@ "support": { "sign": true, "verify": true, - "iss": false, - "sub": false, - "aud": false, + "iss": true, + "sub": true, + "aud": true, "exp": true, "nbf": true, - "iat": false, - "jti": false, + "iat": true, + "jti": true, + "typ": true, "hs256": true, - "hs384": false, + "hs384": true, "hs512": true, "rs256": true, - "rs384": false, - "rs512": false, - "es256": false, - "es384": false, - "es512": false, - "ps256": false, - "ps384": false, - "ps512": false + "rs384": true, + "rs512": true, + "es256": true, + "es384": true, + "es512": true, + "ps256": true, + "ps384": true, + "ps512": true, + "eddsa": true, + "es256k": false }, - "authorUrl": "https://github.com/SkyLothar", - "authorName": "SkyLothar", - "gitHubRepoPath": "SkyLothar/lua-resty-jwt", - "repoUrl": "https://github.com/SkyLothar/lua-resty-jwt", + "authorUrl": "https://github.com/cdbattags", + "authorName": "cdbattags", + "gitHubRepoPath": "cdbattags/lua-resty-jwt", + "repoUrl": "https://github.com/cdbattags/lua-resty-jwt", "installCommandMarkdown": [ - "git clone https://github.com/SkyLothar/lua-resty-jwt.git" + "luarocks install lua-resty-jwt" ], - "stars": 505 + "stars": 155 } ] }, @@ -3990,6 +3996,43 @@ ".package(url: \"https://github.com/beatt83/jose-swift.git\", from: \"2.4.0\")" ], "stars": 21 + }, + { + "minimumVersion": null, + "support": { + "sign": true, + "verify": true, + "iss": true, + "sub": true, + "aud": true, + "exp": true, + "nbf": true, + "iat": true, + "jti": true, + "typ": true, + "hs256": true, + "hs384": true, + "hs512": true, + "rs256": true, + "rs384": true, + "rs512": true, + "es256": true, + "es384": true, + "es512": true, + "es256k": true, + "ps256": true, + "ps384": true, + "ps512": true, + "eddsa": true + }, + "authorUrl": "https://github.com/amosavian", + "authorName": "Amir Abbas Mousavian", + "gitHubRepoPath": "amosavian/JWSETKit", + "repoUrl": "https://github.com/amosavian/JWSETKit", + "installCommandMarkdown": [ + ".package(url: \"https://github.com/amosavian/JWSETKit\", from: \"2.0.0\")" + ], + "stars": 67 } ] } diff --git a/src/features/analytics/components/adobe-analytics-script.component.tsx b/src/features/analytics/components/adobe-analytics-script.component.tsx new file mode 100644 index 00000000..d7f2d8ad --- /dev/null +++ b/src/features/analytics/components/adobe-analytics-script.component.tsx @@ -0,0 +1,10 @@ +import Script from 'next/script'; + +const AdobeAnalyticsScript = () => { + const source = process.env.NEXT_PUBLIC_ADOBE_ANALYTICS_URL; + return source ? ( + + ) : null; +}; + +export default AdobeAnalyticsScript; diff --git a/src/features/common/assets/arrow-head-icon.component.tsx b/src/features/common/assets/arrow-head-icon.component.tsx new file mode 100644 index 00000000..0910b453 --- /dev/null +++ b/src/features/common/assets/arrow-head-icon.component.tsx @@ -0,0 +1,21 @@ +import React from "react"; + +export const ArrowHeadIconComponent: React.FC = () => { + return ( + + + + ); +}; diff --git a/src/features/common/assets/auth0-logo.component.tsx b/src/features/common/assets/auth0-logo.component.tsx new file mode 100644 index 00000000..22f18ce7 --- /dev/null +++ b/src/features/common/assets/auth0-logo.component.tsx @@ -0,0 +1,53 @@ +interface Auth0LogoComponentProps { + title: string; +} + +export const Auth0LogoComponent: React.FC = ({title}) => { + return ( + + {title} + + + + + + + + + + + ); +}; diff --git a/src/features/common/assets/auth0-logo.png b/src/features/common/assets/auth0-logo.png deleted file mode 100644 index 51ff6735..00000000 Binary files a/src/features/common/assets/auth0-logo.png and /dev/null differ diff --git a/src/features/common/assets/download-icon.component.tsx b/src/features/common/assets/download-icon.component.tsx index c2a92fda..f401a279 100644 --- a/src/features/common/assets/download-icon.component.tsx +++ b/src/features/common/assets/download-icon.component.tsx @@ -3,33 +3,26 @@ import React from "react"; export const DownloadIconComponent: React.FC = () => { return ( + > - + > ); }; diff --git a/src/features/common/assets/jwt-flower.png b/src/features/common/assets/jwt-flower.png deleted file mode 100644 index 96b2e512..00000000 Binary files a/src/features/common/assets/jwt-flower.png and /dev/null differ diff --git a/src/features/common/assets/jwt-logo.component.tsx b/src/features/common/assets/jwt-logo.component.tsx new file mode 100644 index 00000000..540dc371 --- /dev/null +++ b/src/features/common/assets/jwt-logo.component.tsx @@ -0,0 +1,82 @@ +export const JwtLogoComponent: React.FC = () => { + return ( + + + + + + + + + + + + + ); +}; \ No newline at end of file diff --git a/src/features/common/assets/jwt-wordmark.component.tsx b/src/features/common/assets/jwt-wordmark.component.tsx new file mode 100644 index 00000000..1a903280 --- /dev/null +++ b/src/features/common/assets/jwt-wordmark.component.tsx @@ -0,0 +1,30 @@ +export const JwtWordmarkComponent: React.FC = () => { + return ( + + + + + + ); +}; \ No newline at end of file diff --git a/src/features/common/components/article/article.module.scss b/src/features/common/components/article/article.module.scss index 97f5a8cf..1f5ef8a4 100644 --- a/src/features/common/components/article/article.module.scss +++ b/src/features/common/components/article/article.module.scss @@ -7,26 +7,13 @@ .content { @include InnerContentGrid; - - padding: 3rem 0 3rem 0; - gap: 2rem; - - @media #{$breakpoint-dimension-sm} { - padding: 3rem 0 4rem 0; - } - - @media #{$breakpoint-dimension-md} { - gap: 3rem; - padding: 4rem 0 4rem 0; - } } .article { display: flex; flex-direction: column; - grid-column: span 6; - max-width: 48.5rem; + max-width: 56rem; justify-self: center; @media #{$breakpoint-dimension-sm} { @@ -47,7 +34,7 @@ font-size: 2rem; line-height: 2.5rem; font-style: normal; - font-weight: 400; + font-weight: 500; letter-spacing: -0.05rem; margin-bottom: 1.5rem; font-family: var(--font-secondary), sans-serif; @@ -68,13 +55,12 @@ h3 { color: var(--color_fg_bold); - font-size: 1.5rem; line-height: 2rem; font-style: normal; - font-weight: 400; + font-weight: 500; letter-spacing: -0.05rem; - margin-bottom: 2rem; + margin-bottom: 1.5rem; font-family: var(--font-secondary), sans-serif; :global(html[lang="ja"]) & { @@ -89,11 +75,11 @@ "clig" off, "liga" off; font-size: 1rem; - line-height: 1.5em; + line-height: 1.45; font-style: normal; font-weight: 400; letter-spacing: -0.005rem; - margin-bottom: 2rem; + margin-bottom: 1rem; :global(html[lang="ja"]) & { @include LocaleLineHeight("ja", 1.5rem); @@ -107,12 +93,10 @@ ul, ol { - list-style-position: inside; margin-top: 0; ul, ol { - list-style-position: inside; margin-left: 1.5rem; padding-left: 0; } @@ -121,11 +105,11 @@ li { margin-bottom: 0.75rem; font-size: 1rem; - line-height: 2rem; + line-height: 1.45; color: var(--color_fg_default); letter-spacing: -0.01px; - text-indent: -1.5rem; - padding-left: 1.5rem; + margin-left: 1rem; + padding-left: 1rem; :global(html[lang="ja"]) & { @include LocaleLineHeight("ja", 2rem); @@ -136,9 +120,9 @@ &, &:hover, &:active { - color: var(--color_fg_link_primary); text-decoration: underline; - font-weight: 400; + font-weight: 500; + text-underline-offset: 0.25rem; } } @@ -148,42 +132,70 @@ } pre { - margin-bottom: 2rem; - background-color: var(--color_bg_code-editor); + margin-bottom: 1rem; + background-color: var(--color_bg_layer_alternate); color: var(--color_fg_default); font-size: 0.875rem; line-height: 1.375rem; font-style: normal; font-weight: 400; letter-spacing: 0.15px; - padding: 0.75rem; + padding: 0.25rem; border: 1px solid var(--color_border_bold); - border-radius: 0.25rem; + border-radius: 1rem; code { - color: var(--color_code_dark_blue2); + display: block; + width: 100%; + color: var(--color_code_gray); background-color: var(--color_bg_code-editor); font-weight: 500; - } - - @media #{$breakpoint-dimension-sm} { - border-radius: 0.5rem; - padding: 1rem; + font-size: 0.875rem; + line-height: 1.35; + border-radius: 0.75rem; + padding: 0.75rem; + box-shadow: + 0 0 0 0.5px rgba(0, 0, 0, 0.12), + 0 2px 2px -1px rgba(0, 0, 0, 0.04), + 0 4px 4px -2px rgba(0, 0, 0, 0.04), + 0 6px 6px -3px rgba(0, 0, 0, 0.04); } } blockquote { - background-color: var(--color_bg_state_info_subtle); - padding: 1rem 1.5rem; - margin: 2rem 0 2rem 0; - border-radius: 0 0.5rem 0.5rem 0; - border-left: 4px solid var(--color_border_state_info); + background-color: var(--color_bg_layer); + font-size: 0.875rem; + padding: 1rem; + margin: 2rem 0; + border-radius: 1rem; + border: 1px solid var(--color_border_default); + box-shadow: + 0 0 0 0.5px rgba(0, 0, 0, 0.12), + 0 2px 2px -1px rgba(0, 0, 0, 0.04), + 0 4px 4px -2px rgba(0, 0, 0, 0.04), + 0 6px 6px -3px rgba(0, 0, 0, 0.04); & * { - color: var(--color_fg_on_state_info_subtle); + color: var(--color_fg_default); padding: 0; margin: 0; } + + p { + display: flex; + flex-direction: row; + font-size: 0.875rem; + align-items: center; + gap: 0.75rem; + } + + ::before { + content: url("data:image/svg+xml,%3Csvg width='12' height='12' viewBox='0 0 12 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6 0.5C9.03757 0.5 11.5 2.96243 11.5 6C11.5 9.03757 9.03757 11.5 6 11.5C2.96243 11.5 0.5 9.03757 0.5 6C0.5 2.96243 2.96243 0.5 6 0.5ZM6 5.25C5.72386 5.25 5.5 5.47386 5.5 5.75V8.75H6.5V5.75C6.5 5.47386 6.27614 5.25 6 5.25ZM6 3.125C5.65482 3.125 5.375 3.40482 5.375 3.75C5.375 4.09518 5.65482 4.375 6 4.375C6.34518 4.375 6.625 4.09518 6.625 3.75C6.625 3.40482 6.34518 3.125 6 3.125Z' fill='%235C5650'/%3E%3C/svg%3E%0A"); + display: block; + padding: 0.25rem 0.4rem 0.125rem; + border-radius: 9999px; + background-color: var(--color_bg_layer_alternate); + } } *:has(+ blockquote) { diff --git a/src/features/common/components/auth0-cta/auth0-cta.component.tsx b/src/features/common/components/auth0-cta/auth0-cta.component.tsx index 2bbb4bca..28248ce7 100644 --- a/src/features/common/components/auth0-cta/auth0-cta.component.tsx +++ b/src/features/common/components/auth0-cta/auth0-cta.component.tsx @@ -2,10 +2,9 @@ import React from "react"; import { getLocalizedSecondaryFont } from "@/libs/theme/fonts"; import clsx from "clsx"; import { Auth0DictionaryModel } from "@/features/localization/models/auth0-dictionary.model"; -import { BoxComponent } from "@/features/common/components/box/box.component"; import styles from "./auth0-cta.module.scss"; import Link from "next/link"; -import NextArrow from "@/features/common/assets/arrow-right.svg"; +import { ArrowHeadIconComponent } from "../../assets/arrow-head-icon.component"; interface Auth0CtaComponentProps { languageCode: string; @@ -17,40 +16,36 @@ export const Auth0CtaComponent: React.FC = ({ dictionary, }) => { return ( - -
-
-
-

- {dictionary.title} -

-

{dictionary.description}

-
- {dictionary.ctaButton && ( - - {dictionary.ctaButton.label} -
- +
+
+
+
+
+
+

+ {dictionary.title} +

+

+ {dictionary.description} +

- - )} + + {dictionary.ctaButton.label} + + +
+
+
-
- +
); }; diff --git a/src/features/common/components/auth0-cta/auth0-cta.module.scss b/src/features/common/components/auth0-cta/auth0-cta.module.scss index 8f79f1ef..ce3da6ff 100644 --- a/src/features/common/components/auth0-cta/auth0-cta.module.scss +++ b/src/features/common/components/auth0-cta/auth0-cta.module.scss @@ -1,11 +1,22 @@ @use "@/libs/theme/styles/variables" as *; @use "@/libs/theme/styles/mixins" as *; -.container { - @include Container; +.cta_container { position: relative; overflow: hidden; background: var(--color_bg_auth0-cta); + grid-column: span 8; + border-radius: 1rem; + max-width: 1008px; + border: 1px solid var(--color_border_default); + box-shadow: 0 2px 12px -4px rgba(0,0,0,.08),0 2px 1px -.5px rgba(0,0,0,.04),0 2px 4px -4px rgba(0,0,0,.05),inset -.5px -.5px 2px rgba(0,0,0,.04); + + @media #{$breakpoint-dimension-md} { + margin: 0 auto; + &:before { + background: none; + } + } &:before { position: absolute; @@ -15,83 +26,20 @@ width: 100%; height: 100%; } - - :global(html[data-theme="light"]) &:before { - background-image: url("/images/mobile.light.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="system-light"]) &:before { - background-image: url("/images/mobile.light.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="dark"]) &:before { - background-image: url("/images/mobile.dark.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="system-dark"]) &:before { - background-image: url("/images/mobile.dark.auth0-flow-bg.svg"); - background-size: cover; - } - - @media #{$breakpoint-dimension-sm} { - :global(html[data-theme="light"]) &:before { - background-image: url("/images/tablet.light.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="system-light"]) &:before { - background-image: url("/images/tablet.light.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="dark"]) &:before { - background-image: url("/images/tablet.dark.auth0-flow-bg.svg"); - background-size: cover; - } - :global(html[data-theme="system-dark"]) &:before { - background-image: url("/images/tablet.dark.auth0-flow-bg.svg"); - background-size: cover; - } - } - - @media #{$breakpoint-dimension-md} { - padding-bottom: 7.5rem; - background: unset; - - &:before { - background: none; - } - - :global(html[data-theme="light"]) &:before { - background-image: none; - } - :global(html[data-theme="system-light"]) &:before { - background-image: none; - } - :global(html[data-theme="dark"]) &:before { - background-image: none; - } - :global(html[data-theme="system-dark"]) &:before { - background-image: none; - } - - &:after { - display: none; - background: none; - } - } } -.wrapper { - @include ExtendedGrid; +.cta_wrapper { + max-width: 1536px; + margin: 0 auto; position: relative; justify-content: center; - - border-radius: 0; + border-radius: 1rem; overflow: hidden; + width: 100%; + height: 100%; @media #{$breakpoint-dimension-md} { background: var(--color_bg_auth0-cta); - border-radius: 1.5rem; - &:before { position: absolute; bottom: 0; @@ -100,83 +48,63 @@ width: 100%; height: 100%; } - - :global(html[data-theme="light"]) &:before { - background: url("/images/light.auth0-flow-bg.svg") no-repeat right - bottom; - background-size: cover; - } - :global(html[data-theme="system-light"]) &:before { - background: url("/images/light.auth0-flow-bg.svg") no-repeat right - bottom; - background-size: cover; - } - :global(html[data-theme="dark"]) &:before { - background: url("/images/dark.auth0-flow-bg.svg") no-repeat right - bottom; - background-size: cover; - } - :global(html[data-theme="system-dark"]) &:before { - background: url("/images/dark.auth0-flow-bg.svg") no-repeat right - bottom; - background-size: cover; - } } } -.content { - @include InnerContentFlex; +.cta_content { + display: flex; justify-content: center; + width: 100%; + height: 100%; - @media #{$breakpoint-dimension-sm} { - @include InnerContentGrid; + @media #{$breakpoint-dimension-md} { + display: grid; + grid-template-columns: repeat(12,minmax(0,1fr)); + width: 100%; } } -.cta { +.cta_cta { z-index: 1; width: 100%; display: flex; flex-direction: column; - padding: 2.5rem 0; + padding: .25rem; align-items: center; gap: 2rem; - @media #{$breakpoint-dimension-sm} { - padding: 3rem 0; - grid-column: 1 / -1; + @media #{$breakpoint-dimension-md} { + grid-column: 1/-1; display: grid; grid-template-columns: subgrid; } - - @media #{$breakpoint-dimension-md} { - padding: 4rem 0; - } } .cta__copy { - grid-column: 1 / -1; + grid-column: 1/-1; display: flex; flex-direction: column; gap: 1.5rem; - align-items: center; - - @media #{$breakpoint-dimension-sm} { - align-items: unset; - } + align-items: flex-start; + padding: 2.5rem; @media #{$breakpoint-dimension-md} { - align-items: center; - flex-direction: row; + grid-column: span 6; + align-items: flex-start; + flex-direction: column; gap: 2.5rem; } + + @media #{$breakpoint-dimension-sm} { + align-items: unset; + } } .cta__text { width: 100%; display: flex; flex-direction: column; - gap: 0.75rem; + gap: .75rem; @media #{$breakpoint-dimension-sm} { gap: 1rem; @@ -187,26 +115,15 @@ width: 100%; margin: 0; text-align: left; - font-size: 2.25rem; - line-height: 2.75rem; - letter-spacing: -0.03rem; - font-weight: 400; - color: var(--color_fg_bold); + font-size: 1.5rem; + line-height: 1.15; + color: #fff; + font-feature-settings: lining-nums proportional-nums; font-variant-numeric: lining-nums proportional-nums; font-style: normal; - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 2.25rem); - } - - @media #{$breakpoint-dimension-sm} { + @media #{$breakpoint-dimension-lg} { font-size: 2.5rem; - line-height: 3rem; - letter-spacing: -0.08rem; - - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 2.5rem); - } } } @@ -216,167 +133,42 @@ line-height: 1.5rem; font-weight: 400; - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 1rem); - } - @media #{$breakpoint-dimension-sm} { max-width: 40rem; - font-size: 1.25rem; - line-height: 1.75rem; - letter-spacing: 0.01rem; - - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 1.25rem); - } + font-size: 1rem; + line-height: 1.5rem; + letter-spacing: -.01rem; } @media #{$breakpoint-dimension-md} { - font-size: 1.25rem; - line-height: 1.75rem; - letter-spacing: 0.1px; - - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 1.25rem); - } + font-size: 1rem; + line-height: 1.5rem; + letter-spacing: -.1px; } } -.cta__media { - position: relative; - grid-column: 1 / -1; - align-self: center; - justify-self: center; +.cta__button { display: flex; - align-items: center; - justify-content: center; - gap: 2.4rem; - - overflow: hidden; - - width: 100%; - - background-size: 100%; - - aspect-ratio: 281 / 547; - - :global(html[lang="en"][data-theme="light"]) & { - background-image: url("/images/mobile.en.light.auth0-flow.svg"); - } - :global(html[lang="en"][data-theme="dark"]) & { - background-image: url("/images/mobile.en.dark.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="light"]) & { - background-image: url("/images/mobile.ja.light.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="dark"]) & { - background-image: url("/images/mobile.ja.dark.auth0-flow.svg"); - } - - :global(html[lang="en"][data-theme="system-light"]) & { - background-image: url("/images/mobile.en.light.auth0-flow.svg"); - } - :global(html[lang="en"][data-theme="system-dark"]) & { - background-image: url("/images/mobile.en.dark.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="system-light"]) & { - background-image: url("/images/mobile.ja.light.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="system-dark"]) & { - background-image: url("/images/mobile.ja.dark.auth0-flow.svg"); - } - - @media #{$breakpoint-dimension-sm} { - aspect-ratio: 672 / 251; - - :global(html[lang="en"][data-theme="light"]) & { - background-image: url("/images/tablet.en.light.auth0-flow.svg"); - } - :global(html[lang="en"][data-theme="dark"]) & { - background-image: url("/images/tablet.en.dark.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="light"]) & { - background-image: url("/images/tablet.ja.light.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="dark"]) & { - background-image: url("/images/tablet.ja.dark.auth0-flow.svg"); - } - - :global(html[lang="en"][data-theme="system-light"]) & { - background-image: url("/images/tablet.en.light.auth0-flow.svg"); - } - :global(html[lang="en"][data-theme="system-dark"]) & { - background-image: url("/images/tablet.en.dark.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="system-light"]) & { - background-image: url("/images/tablet.ja.light.auth0-flow.svg"); - } - :global(html[lang="ja"][data-theme="system-dark"]) & { - background-image: url("/images/tablet.ja.dark.auth0-flow.svg"); - } - } - - @media #{$breakpoint-dimension-md} { - aspect-ratio: 3216 / 861; - - :global(html[lang="en"][data-theme="light"]) & { - background-image: url("/images/en.light.auth0-flow.png"); - } - :global(html[lang="en"][data-theme="dark"]) & { - background-image: url("/images/en.dark.auth0-flow.png"); - } - :global(html[lang="ja"][data-theme="light"]) & { - background-image: url("/images/ja.light.auth0-flow.png"); - } - :global(html[lang="ja"][data-theme="dark"]) & { - background-image: url("/images/ja.dark.auth0-flow.png"); - } - - :global(html[lang="en"][data-theme="system-light"]) & { - background-image: url("/images/en.light.auth0-flow.png"); - } - :global(html[lang="en"][data-theme="system-dark"]) & { - background-image: url("/images/en.dark.auth0-flow.png"); - } - :global(html[lang="ja"][data-theme="system-light"]) & { - background-image: url("/images/ja.light.auth0-flow.png"); - } - :global(html[lang="ja"][data-theme="system-dark"]) & { - background-image: url("/images/ja.dark.auth0-flow.png"); - } - } -} - -.button { - display: flex; - padding: 1rem 2rem; + padding: .25rem .75rem; justify-content: center; align-items: center; - gap: 0.5rem; - flex-shrink: 0; - - border: 1px solid var(--color_border_button_outline); - border-radius: 0.375rem; - background: transparent; - backdrop-filter: blur(1.5rem); - - color: var(--color_fg_on_button_outline); - font-size: 1rem; - line-height: 1.5rem; - font-style: normal; + gap: .5rem; + border-radius: 9999px; + background: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + box-shadow: 0 1px 1px -.5px rgba(0,0,0,.04),0 3px 3px -1.5px rgba(0,0,0,.04),0 6px 6px -3px rgba(0,0,0,.04),0 12px 12px -6px rgba(0,0,0,.04),0 24px 24px -12px rgba(0,0,0,.04),inset 0 0 0 1px rgba(0,0,0,.08); + color: var(--color_fg_default); + font-size: .875rem; + font-weight: 700; + line-height: 1.375rem; font-weight: 500; - letter-spacing: 0.2px; - - width: 100%; - - :global(html[lang="ja"]) & { - @include LocaleLineHeight("ja", 1rem); - } + letter-spacing: -.2px; + transition: all .3s ease-in-out; - &:focus-visible { - outline: solid 1px var(--color_border_focus); - outline-offset: 0.125rem; - border-radius: 0.125rem; + &:hover { + border: 1px solid var(--color_border_bold); + box-shadow: 0 0 0 4px var(--color_button_focus_inverse); + transition: all .2s ease-out; } @media #{$breakpoint-dimension-sm} { @@ -384,34 +176,41 @@ } } -.button__arrow { - flex-shrink: 0; +.cta__media { + position: relative; + grid-column: 1/-1; + align-self: center; + justify-self: center; display: flex; align-items: center; justify-content: center; - height: 0.75rem; - width: 0.75rem; - - svg { - height: 0.75rem; - width: 0.75rem; + gap: 2.4rem; + min-height: 348px; + overflow: hidden; + width: 100%; + height: 100%; + border-radius: .75rem; + background-size: cover; + background-position: 50%; + background-repeat: no-repeat; - path { - fill: var(--color_fg_on_button_outline); - } + :global(html[lang="en"]) & { + background-image: url("/images/en.diagram.jpg"); } - @media #{$breakpoint-dimension-sm} { - height: 1rem; - width: 1rem; + :global(html[lang="ja"]) & { + background-image: url("/images/ja.diagram.jpg"); + } - svg { - height: 0.875rem; - width: 0.875rem; - } + @media #{$breakpoint-dimension-md} { + border-bottom-left-radius: 12rem; + grid-column: span 6; + align-items: flex-start; + flex-direction: column; + gap: 2.5rem; } - &[data-rotation="true"] { - animation: rotate 2s linear infinite; + @media #{$breakpoint-dimension-sm} { + align-items: unset; } } diff --git a/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx index 1f7d4251..3961a806 100644 --- a/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/dark-icon.component.tsx @@ -3,20 +3,18 @@ import React from "react"; export const DarkIconComponent: React.FC = () => { return ( - + ); }; diff --git a/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx b/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx index 7552b6c2..71ee0194 100644 --- a/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx +++ b/src/features/common/components/bars/ribbon/assets/globe-icon.component.tsx @@ -3,10 +3,11 @@ import React from "react"; export const GlobeIconComponent: React.FC = () => { return (
{dictionary.cta.title} = ({
-
- - label={null} - compactLabel={null} - icon={ - isSystemThemePreference(currentTheme.code) ? ( - - ) : isLightThemePreference(currentTheme.code) ? ( - - ) : ( - - ) - } - languageCode={languageCode} - selectedOptionCode={currentTheme.code} - handleSelection={handleThemeSelection} - options={themeOptions} - aria={{ - buttonLabel: dictionary.themePicker.button.ariaLabel, - listLabel: dictionary.themePicker.list.ariaLabel, - }} - /> - {dictionary.languagePicker.options.length > 1 && ( - } - label={currentLanguage.label} - compactLabel={currentLanguage.code.toUpperCase()} - languageCode={languageCode} - selectedOptionCode={languageCode} - handleSelection={handleLanguageSelection} - options={languageOptions} - aria={{ - buttonLabel: dictionary.languagePicker.button.ariaLabel, - listLabel: dictionary.languagePicker.list.ariaLabel, - }} - /> - )} -
- - ); }; diff --git a/src/features/common/components/bars/ribbon/ribbon.module.scss b/src/features/common/components/bars/ribbon/ribbon.module.scss index 8959d4ef..7436d922 100644 --- a/src/features/common/components/bars/ribbon/ribbon.module.scss +++ b/src/features/common/components/bars/ribbon/ribbon.module.scss @@ -1,38 +1,10 @@ @use "@/libs/theme/styles/variables" as *; @use "@/libs/theme/styles/mixins" as *; -.container { - @include Container; - - background: var(--color_bg_app_bar); - border-bottom: 1px solid; - border-image-slice: 1; - border-image-source: linear-gradient( - 135deg, - rgb(76, 183, 163) 0%, - rgb(63, 89, 228) 50%, - rgb(64, 22, 160) 100% - ); -} - -.wrapper { - @include ExtendedGrid; - height: $ribbon-height; -} - -.content { - position: relative; - @include InnerContentFlex; - height: 100%; - grid-column: 1 / -1; - align-items: center; - justify-content: space-between; - - color: $neutrals-light-100-snow; -} - .cta { display: flex; + align-items: center; + justify-content: center; font-size: 0.75rem; line-height: 1.25rem; color: $Neutral0; @@ -88,11 +60,3 @@ } } -.actions { - display: flex; - gap: 0.5rem; - - @media #{$breakpoint-dimension-md} { - gap: 1rem; - } -} diff --git a/src/features/common/components/card-message/card-message.module.scss b/src/features/common/components/card-message/card-message.module.scss index a4a46757..c96542cd 100644 --- a/src/features/common/components/card-message/card-message.module.scss +++ b/src/features/common/components/card-message/card-message.module.scss @@ -4,7 +4,7 @@ .span, .p, .code { - font-size: 0.75rem; + font-size: .875rem; line-height: 1.125rem; } diff --git a/src/features/common/components/card-tabs/card-tabs.component.tsx b/src/features/common/components/card-tabs/card-tabs.component.tsx index 00af36ee..435b1b16 100644 --- a/src/features/common/components/card-tabs/card-tabs.component.tsx +++ b/src/features/common/components/card-tabs/card-tabs.component.tsx @@ -129,16 +129,20 @@ const CardTabs: React.FC = ({ {compactTitle} + {activeCard.slots?.toolbar && ( +
+
+ {activeCard.slots.toolbar} +
+
+ )} ); })} - {activeCard.slots?.toolbar && ( -
- {activeCard.slots.toolbar} -
- )} {cards.map((card) => { return ( @@ -181,7 +185,7 @@ const CardTabs: React.FC = ({ onSelectionChange={(e) => { handleActiveCardChange(e.toString()); }} - className={clsx(MonoFont.className, styles.cardTabs)} + className={styles.cardTabs} > <> @@ -197,16 +201,18 @@ const CardTabs: React.FC = ({ {compactTitle} + {activeCard.slots?.toolbar && ( +
+
+ {activeCard.slots.toolbar} +
+
+ )} ); })}
- {activeCard.slots?.toolbar && ( -
- {activeCard.slots.toolbar} -
- )} {cards.map((card) => { return ( diff --git a/src/features/common/components/card-tabs/card-tabs.module.scss b/src/features/common/components/card-tabs/card-tabs.module.scss index 8e7a0c44..2db5a3b3 100644 --- a/src/features/common/components/card-tabs/card-tabs.module.scss +++ b/src/features/common/components/card-tabs/card-tabs.module.scss @@ -8,46 +8,42 @@ width: 100%; max-height: fit-content; flex-direction: column; - row-gap: 0.5rem; + row-gap: 1rem; cursor: default; } .cardTabs__title { - grid-column: 1 / -1; + grid-column: 1/-1; display: flex; align-items: center; - gap: 0.25rem; - text-transform: uppercase; + gap: .25rem; color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.24px; - margin-right: 8px; + font-size: 1rem; + line-height: 1.15rem; + font-weight: 700; + letter-spacing: -.1px; } .cardTabs { - position: relative; - grid-column: span 6; - display: flex; - height: 100%; - width: 100%; - flex-direction: column; - border-radius: 0.5rem; - border: 1px solid var(--color_border_bold); - background: var(--color_bg_layer_alternate); - overflow: hidden; + grid-column: span 6; + display: flex; + flex-direction: column; + border-radius: 1rem; + border: .5px solid var(--color_border_default); + background: var(--color_bg_layer_alternate-bold); + letter-spacing: -.1px; + gap: .25rem; } $cardTabs__tabList__height: 2.5rem; .cardTabs__tabList { - position: relative; - display: flex; - border-bottom: 1px solid var(--color_border_bold); - height: $cardTabs__tabList__height; - flex-shrink: 0; + position: relative; + display: flex; + height: 2.5rem; + padding: .5rem; + gap: .25rem; } .cardTabs__title__container { @@ -56,23 +52,21 @@ $cardTabs__tabList__height: 2.5rem; } .cardTab__title { - position: relative; - display: flex; - align-items: center; - gap: 0.25rem; - text-transform: uppercase; - padding: 0.5rem 0.75rem; - border-bottom: 0.125rem solid transparent; - color: var(--color_fg_default); - font-size: 0.75rem; - line-height: 1.125rem; - font-weight: 500; - letter-spacing: 0.24px; - cursor: pointer; + display: flex; + align-items: center; + gap: .25rem; + border-radius: .5rem; + padding: 0 .5rem; + color: var(--color_fg_default); + font-size: .875rem; + line-height: 1.125rem; + font-weight: 500; + letter-spacing: -.1px; + cursor: pointer; &[data-selected="true"] { - color: var(--color_fg_selected); - border-bottom: 0.125rem solid var(--color_border_selected); + background-color: var(--color_bg_layer); + box-shadow: 0 0 0 1px rgba(0,0,0,.04),0 1px 1px -.5px rgba(0,0,0,.04),inset 0 0 0 1px rgba(0,0,0,.04); } &[data-focus-visible]:after { @@ -86,6 +80,7 @@ $cardTabs__tabList__height: 2.5rem; .cardTab__title__full { display: none; + font-weight: 500; @media #{$breakpoint-dimension-sm} { display: flex; @@ -99,14 +94,24 @@ $cardTabs__tabList__height: 2.5rem; } .cardTabs__actionButtons { - position: absolute; - right: 0.75rem; - top: 0; - display: flex; - align-items: center; - justify-content: center; - gap: 0.5rem; - height: $cardTabs__tabList__height; + position: absolute; + right: 0.75rem; + top: 50%; + transform: translateY(-50%); + display: flex; + align-items: center; + justify-content: center; + gap: 0.5rem; +} + +.cardTabs__tooltipContainer { + position: relative; + display: inline-block; + &:hover { + .button__tooltip { + opacity: 1; + } + } } .cardTabs__body { diff --git a/src/features/common/components/card-toolbar-button/card-toolbar-button.component.tsx b/src/features/common/components/card-toolbar-button/card-toolbar-button.component.tsx index 212703a2..5742102d 100644 --- a/src/features/common/components/card-toolbar-button/card-toolbar-button.component.tsx +++ b/src/features/common/components/card-toolbar-button/card-toolbar-button.component.tsx @@ -7,12 +7,13 @@ import { MonoFont } from "@/libs/theme/fonts"; export interface CardToolbarButtonComponentProps extends AriaButtonOptions<"span"> { variant: "standard" | "icon"; + tooltipText?: string; } export const CardToolbarButtonComponent: React.FC< PropsWithChildren > = (props) => { - const { variant, children } = props; + const { variant, tooltipText, children } = props; const ref = useRef(null); @@ -22,20 +23,26 @@ export const CardToolbarButtonComponent: React.FC< elementType: "span", preventFocusOnPress: true, }, - ref, + ref ); return ( - + {tooltipText && ( + {tooltipText} )} - data-style="compact" - > - {children} - +
); }; diff --git a/src/features/common/components/card-toolbar-button/card-toolbar-button.module.scss b/src/features/common/components/card-toolbar-button/card-toolbar-button.module.scss index b4fb851e..eff9edaf 100644 --- a/src/features/common/components/card-toolbar-button/card-toolbar-button.module.scss +++ b/src/features/common/components/card-toolbar-button/card-toolbar-button.module.scss @@ -1,25 +1,44 @@ +.button__tooltipContainer { + position: relative; + display: inline-block; + &:hover { + .button__tooltip { + opacity: 1; + } + } +} .button__standard { - gap: 0.75rem; - border-radius: 0.25rem; - border: 1px solid var(--color_border_button); + gap: .75rem; + border-radius: .5rem; color: var(--color_fg_on_button_subtle); - font-variant-numeric: slashed-zero; - font-feature-settings: - "clig" off, - "liga" off; + z-index: 2; background-color: transparent; text-transform: uppercase; cursor: pointer; - letter-spacing: 0.2px; + padding: .75rem; + letter-spacing: .2px; appearance: none; display: flex; align-items: center; + justify-content: center; + border: 1px solid transparent; + transition: all .2s ease-out; + width: 2rem; + height: 2rem; + font-size: .875rem; + line-height: 1.375rem; &[aria-disabled="true"] { opacity: 0.48; cursor: not-allowed; } + &:hover { + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + transition: all .2s ease-out; + } + padding: 0 0.75rem; width: 5rem; height: 2rem; @@ -27,11 +46,11 @@ line-height: 1.375rem; &[data-style="compact"] { - padding: 0.125rem 0.375rem; - width: unset; - height: unset; - font-size: 0.75rem; - line-height: 1.25rem; + padding: .25rem; + width: 2rem; + height: 2rem; + font-size: .8125rem; + line-height: 1.35; } svg { @@ -46,6 +65,25 @@ } } +.button__tooltip { + position: absolute; + top: -30px; + left: 50%; + transform: translateX(-50%); + padding: 4px 8px; + color: var(--color_fg_bold); + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + border-radius: .5rem; + font-size: .75rem; + white-space: nowrap; + opacity: 0; + pointer-events: none; + transition: opacity .2s ease-in-out; + box-shadow: 0 2px 4px rgba(0,0,0,.1); + z-index: 1000; +} + .button__icon { gap: 0.75rem; border-radius: 0.25rem; @@ -72,10 +110,9 @@ } &:hover { - background-color: var(--color_bg_layer_alternate-bold); - svg { - stroke: var(--color_fg_selected); - } + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + transition: all .2s ease-out; } &:focus-visible { diff --git a/src/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component.tsx b/src/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component.tsx index bb046555..2afb7879 100644 --- a/src/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component.tsx +++ b/src/features/common/components/card-toolbar-buttons/card-toolbar-clear-button/card-toolbar-clear-button.component.tsx @@ -4,6 +4,7 @@ import { CardToolbarButtonComponent, CardToolbarButtonComponentProps, } from "@/features/common/components/card-toolbar-button/card-toolbar-button.component"; +import { ClearIcon } from "../../icons/clear/clear-icon"; interface CardToolbarClearButtonComponentProps extends Omit { @@ -16,8 +17,12 @@ export const CardToolbarClearButtonComponent: React.FC< const dictionary = getButtonsUiDictionary(languageCode); return ( - - {dictionary.clearButton.label} + + ); }; diff --git a/src/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component.tsx b/src/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component.tsx index 788e411a..757c73b4 100644 --- a/src/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component.tsx +++ b/src/features/common/components/card-toolbar-buttons/card-toolbar-copy-button/card-toolbar-copy-button.component.tsx @@ -4,6 +4,8 @@ import { CardToolbarButtonComponentProps, } from "@/features/common/components/card-toolbar-button/card-toolbar-button.component"; import { getButtonsUiDictionary } from "@/features/localization/services/ui-language-dictionary.service"; +import { CopyIcon } from "../../icons/copy/copy-icon"; +import { CheckIcon } from "../../icons/check/check-icon"; interface CardToolbarCopyButtonComponentProps extends Omit, "variant"> { @@ -15,7 +17,6 @@ export const CardToolbarCopyButtonComponent: React.FC< CardToolbarCopyButtonComponentProps > = ({ languageCode, value, isDisabled, ...props }) => { const dictionary = getButtonsUiDictionary(languageCode); - const [isCopied, setIsCopied] = useState(false); const resetCopy = () => { @@ -36,10 +37,13 @@ export const CardToolbarCopyButtonComponent: React.FC< onPress={copyValue} onBlur={resetCopy} variant="standard" + tooltipText={ + isCopied + ? dictionary.copyButton.done.label + : dictionary.copyButton.idle.label + } > - {isCopied - ? dictionary.copyButton.done.label - : dictionary.copyButton.idle.label} + {isCopied ? : } ); }; diff --git a/src/features/common/components/card-toolbar-buttons/card-toolbar-description-button/card-toolbar-description-button.component.tsx b/src/features/common/components/card-toolbar-buttons/card-toolbar-description-button/card-toolbar-description-button.component.tsx index 0cc9e1ba..0304d2ca 100644 --- a/src/features/common/components/card-toolbar-buttons/card-toolbar-description-button/card-toolbar-description-button.component.tsx +++ b/src/features/common/components/card-toolbar-buttons/card-toolbar-description-button/card-toolbar-description-button.component.tsx @@ -11,6 +11,8 @@ import { CardToolbarButtonComponent, CardToolbarButtonComponentProps, } from "@/features/common/components/card-toolbar-button/card-toolbar-button.component"; +import { EyeOpenIcon } from "../../icons/eye-open/eye-open-icon"; +import { EyeCloseIcon } from "../../icons/eye-close/eye-close-icon"; interface CardToolbarDescriptionButtonComponentProps extends Omit { @@ -26,12 +28,12 @@ export const CardToolbarDescriptionButtonComponent: React.FC< const setDescriptionVisibility$ = useDebuggerStore((state) => outputType === "header" ? state.setDecodedHeaderDescriptionVisibility$ - : state.setDecodedPayloadDescriptionVisibility$, + : state.setDecodedPayloadDescriptionVisibility$ ); const descriptionVisibility$ = useDebuggerStore((state) => outputType === "header" ? state.decodedHeaderDescriptionVisibility$ - : state.decodedPayloadDescriptionVisibility$, + : state.decodedPayloadDescriptionVisibility$ ); const [descVisibility, setDescVisibility] = @@ -47,7 +49,7 @@ export const CardToolbarDescriptionButtonComponent: React.FC< ClaimDescriptionVisibilityValues.HIDDEN, { secure: true, - }, + } ); setDescriptionVisibility$(ClaimDescriptionVisibilityValues.HIDDEN); @@ -63,7 +65,7 @@ export const CardToolbarDescriptionButtonComponent: React.FC< ClaimDescriptionVisibilityValues.VISIBLE, { secure: true, - }, + } ); setDescriptionVisibility$(ClaimDescriptionVisibilityValues.VISIBLE); @@ -79,15 +81,22 @@ export const CardToolbarDescriptionButtonComponent: React.FC< - {descVisibility === ClaimDescriptionVisibilityValues.VISIBLE - ? dictionary.hideDetailsButton.label - : dictionary.showDetailsButton.label} + {descVisibility === ClaimDescriptionVisibilityValues.VISIBLE ? ( + + ) : ( + + )} ); }; diff --git a/src/features/common/components/card/card.component.tsx b/src/features/common/components/card/card.component.tsx index bb9e5912..39d92ed9 100644 --- a/src/features/common/components/card/card.component.tsx +++ b/src/features/common/components/card/card.component.tsx @@ -1,14 +1,21 @@ import React, { PropsWithChildren, useId } from "react"; import styles from "./card.module.scss"; import { clsx } from "clsx"; -import { getLocalizedSecondaryFont, MonoFont } from "@/libs/theme/fonts"; +import { getLocalizedSecondaryFont } from "@/libs/theme/fonts"; import { CardMessageComponent } from "@/features/common/components/card-message/card-message.component"; +import { HeaderIcon } from "../icons/header/header-icon"; +import { CheckIcon } from "../icons/check/check-icon"; +import { EncodingFormatToggleSwitchComponent } from "@/features/decoder/components/encoding-format-toggle-swith/encoding-format-toggle-switch"; +import { useDecoderStore } from "@/features/decoder/services/decoder.store"; +import { isHmacAlg } from "../../services/jwt.service"; +import { TokenDecoderKeyFormatPickerComponent } from "@/features/decoder/components/token-decoder-key-format-picker.component"; export interface CardComponentProps extends PropsWithChildren { id: string; languageCode: string; title: string; compactTitle: string; + hasHeaderIcon?: boolean; options: Partial<{ noPadding: boolean; fullWidth: boolean; @@ -44,6 +51,7 @@ export const CardComponent: React.FC = (props) => { }, messages, slots, + hasHeaderIcon = false, } = props; const cardId = useId(); @@ -61,12 +69,12 @@ export const CardComponent: React.FC = (props) => { aria-label={canRenderTitle ? undefined : title} heap-ignore="true" className={clsx( - MonoFont.className, styles.card, options && options.fullWidth && styles.card__hasFullWidth, - options && options.fullHeight && styles.card__hasFullHeight, + options && options.fullHeight && styles.card__hasFullHeight )} data-type={options && options.isOutput ? "output" : "input"} + data-no-padding={options ? options.noPadding : undefined} data-frameless={options && options.frameless} > {!options?.hideTitle && ( @@ -74,21 +82,9 @@ export const CardComponent: React.FC = (props) => { {titleKey && (
{titleKey && !compactTitle && ( -

- - {titleKey} - {titleValue && `: `} - - {titleValue && ( - - {titleValue} - - )} -

- )} - {titleKey && compactTitle && ( - <> -

+
+ {hasHeaderIcon && } +

{titleKey} {titleValue && `: `} @@ -99,6 +95,24 @@ export const CardComponent: React.FC = (props) => { )}

+
+ )} + {titleKey && compactTitle && ( + <> +
+ {hasHeaderIcon && } +

+ + {titleKey} + {titleValue && `: `} + + {titleValue && ( + + {titleValue} + + )} +

+

{compactTitle} @@ -111,6 +125,16 @@ export const CardComponent: React.FC = (props) => { )} )} +
+
+ {children} +
+
{messages && messages.errors && messages.errors.length > 0 ? (
= (props) => { > {messages.success.map((line, index) => { return ( - {line} +
+ + {line} +
); })}
) : null} + {slots?.notification} {messages && messages.warnings && messages.warnings.length > 0 && (
= (props) => { })}
)} - {slots?.notification} -
- {children} -
{slots?.footer && (
{slots.footer}
)} @@ -183,31 +202,40 @@ export const CardWithHeadlineComponent: React.FC< CardWithHeadlineComponentProps > = ({ sectionHeadline, languageCode, ...props }) => { const regionId = useId(); + const alg$ = useDecoderStore((state) => state.alg); return (
{sectionHeadline && ( - <> -

- {sectionHeadline.title} - {sectionHeadline.titleTag && ( - - {sectionHeadline.titleTag} - +
+
+

+ {sectionHeadline.title} + {sectionHeadline.titleTag && ( + + {sectionHeadline.titleTag} + + )} +

+ {sectionHeadline.description && ( +

+ {sectionHeadline.description} +

)} -

- {sectionHeadline.description && ( -

- {sectionHeadline.description} -

+
+ + {isHmacAlg(alg$) ? ( + + ) : ( + )} - +

)}
diff --git a/src/features/common/components/card/card.module.scss b/src/features/common/components/card/card.module.scss index a320b31e..a7104dc9 100644 --- a/src/features/common/components/card/card.module.scss +++ b/src/features/common/components/card/card.module.scss @@ -2,17 +2,17 @@ @use "@/libs/theme/styles/mixins" as *; .cardHeadline__title { - grid-column: 1 / -1; - display: flex; - align-items: center; - gap: 0.25rem; - text-transform: uppercase; - padding: 0.5rem 0; - color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.24px; + grid-column: 1/-1; + display: flex; + align-items: center; + gap: .25rem; + font-family: var(--font-primary); + padding-top: .75rem; + color: var(--color_fg_default); + font-size: 1rem; + line-height: 1.15rem; + font-weight: 700; + letter-spacing: -.2px; & * { font-size: inherit; @@ -56,24 +56,44 @@ } } +.headline__container { + display: flex; + flex-direction: column; + gap: 0; +} + +.title__container { + grid-column: 1/-1; + width: 100%; + color: var(--color_fg_bold); + font-size: 1.75rem; + font-weight: 500; + line-height: 1.5; + margin-bottom: .5rem; + display: flex; + justify-content: space-between; +} + .card { isolation: isolate; + flex: 1 1; grid-column: span 6; display: flex; - flex: 1; flex-direction: column; - border-radius: 0.5rem; - border: 1px solid var(--color_border_bold); - background: var(--color_bg_code-editor); - overflow: hidden; - - &[data-type="output"] { - background: var(--color_bg_layer_alternate); - } + border-radius: 1rem; + border: 1px solid var(--color_border_default); + background: var(--color_bg_layer_alternate-bold); + padding: 0.25rem; + letter-spacing: -.1px; +} - &[data-frameless="true"] { - border-radius: 0; - border: none; +.card__heading_title_container { + display: flex; + flex-direction: row; + align-items: center; + svg { + color: var(--color_fg_default); + margin-right: 2px; } } @@ -106,10 +126,7 @@ justify-content: space-between; padding: 0.5rem 1rem; height: 3rem; - - border-bottom: 1px solid var(--color_border_bold); gap: 0.25rem; - text-transform: uppercase; } .card__fullTitle { @@ -143,22 +160,19 @@ } .card__titleKey { - color: var(--color_fg_default); - font-size: 0.875rem; - line-height: 1.375rem; - font-style: normal; - font-weight: 500; - letter-spacing: 0.24px; - text-transform: uppercase; + color: var(--color_fg_default); + font-size: .875rem; + line-height: 1.375rem; + font-weight: 500; + letter-spacing: .24px; } .card__titleValue { color: var(--color_fg_default); - font-size: 0.875rem; + font-size: .875rem; line-height: 1.375rem; font-weight: 500; - letter-spacing: 0.24px; - text-transform: uppercase; + letter-spacing: .24px; } .card__action { @@ -173,6 +187,13 @@ border-top: 1px solid var(--color_border_bold); } +.card__content { + background-color: var(--color_bg_layer); + height: 100%; + border-radius: .75rem; + box-shadow: 0 0 0 .5px rgba(0,0,0,.12),0 2px 2px -1px rgba(0,0,0,.04),0 4px 4px -2px rgba(0,0,0,.04),0 6px 6px -3px rgba(0,0,0,.04); +} + .card__body { display: flex; flex: 1; @@ -197,20 +218,19 @@ } .card__status { + flex: 1 1; display: flex; - flex-direction: column; - flex-shrink: 0; - padding: 0.5rem 1rem; - gap: 0.25rem; - font-size: 0.875rem; + align-items: center; + justify-content: flex-start; + padding: .75rem .75rem .5rem; + gap: .25rem; + font-size: .875rem; line-height: 1.375rem; color: var(--color_fg_default); - border-top: 1px solid var(--color_border_bold); } .card__success { - background-color: var(--color_bg_state_success_subtle); - + svg, span, p, code { @@ -227,8 +247,7 @@ } .card__error { - background-color: var(--color_bg_state_danger_subtle); - + span, p, code { @@ -245,7 +264,6 @@ } .card__warning { - background-color: var(--color_bg_state_caution_subtle); span, p, diff --git a/src/features/common/components/checkbox/checkbox.module.scss b/src/features/common/components/checkbox/checkbox.module.scss index 307edf43..94b4e498 100644 --- a/src/features/common/components/checkbox/checkbox.module.scss +++ b/src/features/common/components/checkbox/checkbox.module.scss @@ -2,6 +2,7 @@ --selected-color: var(--color_bg_state_success); --selected-color-pressed: var(--color_fg_on_state_success_subtle); --checkmark-color: var(--color_border_state_success); + width: 250px; display: flex; /* This is needed so the HiddenInput is positioned correctly */ diff --git a/src/features/common/components/context-menu/context-menu.module.scss b/src/features/common/components/context-menu/context-menu.module.scss new file mode 100644 index 00000000..8bfe011b --- /dev/null +++ b/src/features/common/components/context-menu/context-menu.module.scss @@ -0,0 +1,61 @@ +.container { + max-width: calc(-24px + 100vw); + max-height: calc(-24px + 100vh); + position: fixed; + background-color: var(--color_bg_layer); + border: 1px solid var(--color_border_default); + font-size: 0.875rem; + z-index: 9009; + cursor: default; + border-radius: 1rem; + overflow: hidden; + padding: 0.25rem; + min-width: 180px; + box-shadow: + 0 1px 1px -0.5px rgba(0, 0, 0, 0.04), + 0 3px 3px -1.5px rgba(0, 0, 0, 0.04), + 0 6px 6px -3px rgba(0, 0, 0, 0.04), + 0 12px 12px -6px rgba(0, 0, 0, 0.04), + inset 0 0 0 1px rgba(0, 0, 0, 0.04); +} + +.groupLabel { + width: 100%; + padding: 0.5rem 0.75rem 0.25rem; + font-size: 0.8125rem; + color: var(--color_fg_default); +} + +.list { + display: flex; + align-items: center; + list-style-type: none; + flex-direction: column; +} + +.menuItem { + width: 100%; + justify-content: flex-start; + position: relative; + padding: 0.5rem 0.75rem; + border-radius: 0.75rem; + color: var(--color_fg_bold); + gap: 0.5rem; + font-family: var(--font-primary); + border: none; + background-color: var(--color_bg_layer); + display: flex; + align-items: center; + + &:hover { + cursor: pointer; + background-color: var(--color_bg_layer_alternate-bold); + } +} + +.separator { + width: 100%; + height: 1px; + background-color: var(--color_border_default); + margin: 0.25rem 0; +} diff --git a/src/features/common/components/context-menu/context-menu.tsx b/src/features/common/components/context-menu/context-menu.tsx new file mode 100644 index 00000000..8ecb97ba --- /dev/null +++ b/src/features/common/components/context-menu/context-menu.tsx @@ -0,0 +1,101 @@ +"use client"; + +import React, { FC } from "react"; +import { BrandDictionaryModel } from "@/features/localization/models/brand-dictionary.model"; +import styles from "./context-menu.module.scss"; +import jwtLogoString from "@/public/img/jwt-logo.svg?raw"; +import jwtSymbolString from "@/public/img/jwt-symbol.svg?raw"; +import jwtWordmarkString from "@/public/img/jwt-wordmark.svg?raw"; +import { Button } from "react-aria-components"; +import { CopyIcon } from "../icons/copy/copy-icon"; +import { DownloadIconComponent } from "../../assets/download-icon.component"; + +interface ContextMenuProps { + dictionary: BrandDictionaryModel; + position: { x: number; y: number } | null; + setIsCopied: (value: React.SetStateAction) => void; +} + +const ContextMenu: FC = ({ + dictionary, + position, + setIsCopied, +}) => { + if (!position) return null; + + const handleIconCopy = async (svgString: string) => { + if (!navigator.clipboard) { + console.error("Clipboard API not available"); + return; + } + try { + await navigator.clipboard.writeText(svgString); + setIsCopied(true); + } catch (err) { + console.error("Failed to copy SVG: ", err); + } + }; + + return ( +
+
{dictionary.menu.brand.label}
+
+ + + + {dictionary.menu.brand.svg.downloadLabel} + + + + + {dictionary.menu.brand.symbol.downloadLabel} + + + + + {dictionary.menu.brand.wordmark.downloadLabel} + +
+
+
{dictionary.menu.tools.label}
+ {dictionary.menu.tools.items.map((el) => ( + {el.label} + ))} +
+ ); +}; + +export default ContextMenu; diff --git a/src/features/common/components/debugger-picker/debugger-picker.component.tsx b/src/features/common/components/debugger-picker/debugger-picker.component.tsx index df256aa4..c62873c2 100644 --- a/src/features/common/components/debugger-picker/debugger-picker.component.tsx +++ b/src/features/common/components/debugger-picker/debugger-picker.component.tsx @@ -5,7 +5,6 @@ import { DebuggerPickerOptionModel } from "@/features/common/models/debugger-pic import { LibraryFilterLabel } from "@/features/libraries/models/library-filters.model"; import { isGroupedOptionsType } from "./utils"; - interface PickerLabelProps { label: string | null; } @@ -17,12 +16,12 @@ const getGroupLabel = ( >, selected: DebuggerPickerOptionModel ): LibraryFilterLabel | undefined => { - if(!isGroupedOptionsType(options)) return undefined + if (!isGroupedOptionsType(options)) return undefined; const group = (options as GroupBase[]).find( (group) => group.options.some((opt) => opt.value === selected.value) ); - return group ? group.label as LibraryFilterLabel : undefined; + return group ? (group.label as LibraryFilterLabel) : undefined; }; const PickerLabel: React.FC = ({ label }) => { @@ -41,7 +40,7 @@ interface DebuggerPickerComponentProps { GroupBase >; isGrouped?: boolean; - selectedOptionCode: DebuggerPickerOptionModel | null; + selectedOptionCode?: DebuggerPickerOptionModel | null; handleSelection: ( selection: string, parentLabel?: LibraryFilterLabel @@ -55,16 +54,14 @@ export const DebuggerPickerComponent: React.FC< > = ({ label, options, - handleSelection, selectedOptionCode, + handleSelection, placeholder, minWidth, }) => { const [isClient, setIsClient] = useState(false); - const handleChange = ( - selection: SingleValue - ) => { + const handleChange = (selection: SingleValue) => { if (!selection) { return; } @@ -78,31 +75,45 @@ export const DebuggerPickerComponent: React.FC< if (!isClient) { return ( -
+ <>
-
+ ); } return ( -
+ <> {label && } -
+ ); }; diff --git a/src/features/common/components/debugger-picker/debugger-picker.module.scss b/src/features/common/components/debugger-picker/debugger-picker.module.scss index 8541f752..ab25a72c 100644 --- a/src/features/common/components/debugger-picker/debugger-picker.module.scss +++ b/src/features/common/components/debugger-picker/debugger-picker.module.scss @@ -21,8 +21,8 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); background: transparent; border: none; color: var(--color_fg_bold); - cursor: pointer; - gap: 0.75rem; + gap: .75rem; + width: 100%; //width: $picker-width-sm; @@ -34,20 +34,17 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); } .picker__label { - display: flex; - align-items: center; - justify-content: center; - padding: 0; - list-style-type: none; - margin: 0; - gap: 0.5rem; - font-size: 0.8125rem; - line-height: 1.3125rem; - letter-spacing: -0.05px; - - width: 100%; - - height: 100%; + display: flex; + align-items: center; + justify-content: flex-start; + padding: 0; + list-style-type: none; + margin: 0; + gap: .5rem; + font-size: .875rem; + line-height: 1.35; + width: 100%; + height: 100%; & svg { stroke: var(--color_fg_bold); @@ -56,4 +53,5 @@ $picker-list-offset-lg: calc(($picker-list-width-lg - $picker-width-lg) / 2); .picker__fullName { display: flex; -} + font-weight: 600; +} \ No newline at end of file diff --git a/src/features/common/components/ebook/ebook.component.tsx b/src/features/common/components/ebook/ebook.component.tsx index b76bdd2b..8a0b20e0 100644 --- a/src/features/common/components/ebook/ebook.component.tsx +++ b/src/features/common/components/ebook/ebook.component.tsx @@ -22,11 +22,44 @@ export const EbookComponent: React.FC = ({ wrapperClassName={styles.wrapper} contentClassName={styles.content} > +
+        
+          {`{\n       "sub"`}
+          : 
+          {`"1234567890"`}
+          ,
+        
+        
+          {" "}
+          {`      "name"`}
+          : 
+          {`"John Doe"`}
+          ,
+        
+        
+          {" "}
+          {`      "admin"`}
+          : 
+          true
+          ,
+        
+        
+          {" "}
+          {`      "iat"`}
+          : 
+          1516239022
+        
+        {`}`}
+      
{dictionary.title} diff --git a/src/features/common/components/ebook/ebook.module.scss b/src/features/common/components/ebook/ebook.module.scss index f17afce6..ca108896 100644 --- a/src/features/common/components/ebook/ebook.module.scss +++ b/src/features/common/components/ebook/ebook.module.scss @@ -2,10 +2,10 @@ @use "@/libs/theme/styles/mixins" as *; .container { - @include Container; - position: relative; - overflow: hidden; - background: var(--color_bg_auth0-cta); + width: 100%; + position: relative; + margin: 4rem 0; + overflow: clip; &:before { position: absolute; @@ -14,99 +14,75 @@ content: ""; width: 100%; height: 100%; - - background: url("https://cdn.auth0.com/website/passkeys-playground/assets/cta-mobile.png") - no-repeat; - background-size: cover; - } - - @media only screen and (min-width: 480px) { - &:before { - background: url("https://cdn.auth0.com/website/passkeys-playground/assets/cta-mobile.png") - no-repeat; - background-size: cover; - } - } - - @media #{$breakpoint-dimension-sm} { - &:before { - background: url("/images/auth0-background.svg") no-repeat right - center; - background-size: cover; - } } @media #{$breakpoint-dimension-md} { - background: unset; - - &:before { - background: none; - } - - &:after { - display: none; - background: none; - } + overflow: unset; } + } .wrapper { @include ExtendedGrid; position: relative; justify-content: center; - - border-radius: 0; - overflow: hidden; - - @media #{$breakpoint-dimension-xl} { - border-radius: 1.5rem; - } + border-radius: 1rem; } .content { - @include InnerContentGrid; + display: flex; + width: calc(100% - 2rem); + max-width: 1312px; + margin: 0 auto; position: relative; padding: 1.5rem 0 14rem; - isolation: isolate; + border-radius: 1rem; + border: 1px dashed var(--color_border_bold); + background-color: var(--color_bg_layer_alternate); + background-image: linear-gradient(90deg,hsla(0,0%,50%,.071) 1px,transparent 0),linear-gradient(180deg,hsla(0,0%,50%,.071) 1px,transparent 0); + background-size: 24px 24px; + &:after { position: absolute; content: ""; - bottom: -1.5rem; - right: 0; - background: url("/images/ebook-image.png") center no-repeat; - background-size: contain; - width: 100%; - height: 75%; + top: 10rem; + left: 18rem; + background-image: url("/images/ebook.svg"); + border-radius: .25rem; + transform: rotate(-7.5deg); + height: 320px; + width: 240px; + z-index: 10; } - @media #{$breakpoint-dimension-xs} { - padding-bottom: 1.5rem; - column-gap: 6rem; + &:hover:after { + perspective: 100px; + transform: rotate(5deg) translateX(-24px) translateY(0) scale(1.1); + transition: all .2s ease-in-out; + } - &:after { - position: absolute; - content: ""; - top: 0; - right: 0; - background: url("/images/ebook-image.png") center no-repeat; - background-size: cover; - height: 100%; - width: 32%; - z-index: 10; - } + @media #{$breakpoint-dimension-xs} { + column-gap: 0; } @media #{$breakpoint-dimension-xs} { column-gap: 0; } + @media only screen and (min-width: 992px) and (min-width: 768px) { + width: calc(100% - 4rem); + grid-template-columns: repeat(12, minmax(0, 1fr)); + } + @media #{$breakpoint-dimension-md} { - background: var(--color_bg_auth0-cta); - padding: 2rem 2.5rem; + padding: 4rem 2.5rem; border-radius: 1rem; - overflow: hidden; - + display: grid; + max-width: 1312px; + margin: 0 auto; + transition: all .2s ease-in-out; + &:before { position: absolute; bottom: 0; @@ -114,68 +90,175 @@ content: ""; width: 100%; height: 100%; - background-size: contain; + border-radius: 1rem; + background-color: var(--color_bg_layer_alternate); + background-image: linear-gradient(90deg,hsla(0,0%,50%,.071) 1px,transparent 0),linear-gradient(180deg,hsla(0,0%,50%,.071) 1px,transparent 0); + background-size: 24px 24px; + } - background: url("/images/auth0-background.svg") no-repeat right - bottom; - background-size: cover; + &:after { + position: absolute; + content: ""; + top: -1.75rem; + left: 4rem; + background-image: url("/images/ebook.svg"); + border-radius: .25rem; + transform: rotate(-7.5deg); + height: 320px; + width: 240px; + z-index: 10; + transition: all .2s ease-in-out; } } + + @media #{$breakpoint-dimension-sm} { + width: calc(100% - 4rem); + } +} + +.ebookBanner__code { + pointer-events: none; + position: absolute; + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: center; + grid-column: 3/span 3; + height: fit-content; + transform: translateY(0); + top: auto; + bottom: 0; + left: 0; + width: 100%; + text-wrap: wrap; + padding: 1rem; + background: #191919; + color: var(--color_fg_code); + border-radius: 1.25rem; + border-top-left-radius: .25rem; + border-top-right-radius: .25rem; + border: 1px solid var(--color_border_bold); + font-size: .875rem; + white-space: pre; + word-wrap: break-word; + text-overflow: ellipsis; + text-wrap-mode: wrap; + text-wrap-style: balance; + overflow: clip; + box-shadow: 0 0 0 1px rgba(0,0,0,.12),0 4px 4px -2px rgba(0,0,0,.04),0 2px 2px -1px rgba(0,0,0,.04); + transition: all .4s ease-in-out; + + span { + padding: .25rem 0; + transition: color .1s ease; + } + + @media #{$breakpoint-dimension-md} { + border-radius: 1.25rem; + left: 0; + top: 50%; + transform: translateY(-50%); + } +} + +.ebookBanner__code_dot { + display: inline; + position: relative; + color: var(--color_border_inverse_static); + padding: .125rem 0; +} + +.ebookBanner__code_string { + display: inline; + position: relative; + color: var(--color_jwt_signature); + padding: .125rem 0; +} + +.ebookBanner__code_boolean { + display: inline; + position: relative; + color: var(--color_jwt_header); + padding: .125rem 0; +} + +.ebookBanner__code_number { + display: inline; + position: relative; + color: var(--color_jwt_payload); + padding: .125rem 0; } .ebookBanner__copy { - grid-column: 1 / span 6; + grid-column: 1/span 6; display: flex; flex-direction: column; + padding: 0 2.5rem; z-index: 1; + margin-top: 2rem; @media #{$breakpoint-dimension-xs} { - grid-column: 1 / span 4; + grid-column: 1/span 4; } @media #{$breakpoint-dimension-sm} { - grid-column: 1 / span 8; + grid-column: 6/span 6; + } + + @media #{$breakpoint-dimension-md} { + margin-top: 0; } } .ebookBanner__title { - color: var(--color_fg_bold); - + width: 100%; + margin: 0; + text-align: left; font-size: 1.5rem; - line-height: 2rem; - letter-spacing: 0.05px; + line-height: 1.15; font-weight: 500; + color: var(--color_fg_bold); + font-feature-settings: lining-nums proportional-nums; + font-variant-numeric: lining-nums proportional-nums; + font-style: normal; + + @media #{$breakpoint-dimension-lg} { + font-size: 2.5rem; + } } .ebookBanner__description { color: var(--color_fg_default); font-size: 1rem; - line-height: 1.25rem; - margin-top: 0.5rem; + line-height: 1.5; + margin-top: .5rem; } .ebookBanner__link { - margin-top: 1.5rem; - display: flex; - padding: 0.5rem 1rem; - justify-content: center; - align-items: center; - gap: 0.5rem; - align-self: stretch; - - border-radius: 0.375rem; - background: linear-gradient( - 120deg, - #191919 -29.78%, - #4016a0 11.61%, - #3f59e4 100.07% - ); - - color: $neutrals-light-100-snow; - font-size: 0.875rem; - line-height: 1.375rem; - font-weight: 500; - letter-spacing: 0.2px; + width: fit-content; + margin-top: 1.5rem; + display: flex; + padding: .25rem .75rem; + justify-content: center; + align-items: center; + gap: .5rem; + align-self: stretch; + border-radius: 9999px; + background: var(--color_fg_on_button); + box-shadow: 0 1px 1px -.5px rgba(0,0,0,.04),0 3px 3px -1.5px rgba(0,0,0,.04),0 6px 6px -3px rgba(0,0,0,.04),0 12px 12px -6px rgba(0,0,0,.04),0 24px 24px -12px rgba(0,0,0,.04),inset 0 0 0 1px rgba(0,0,0,.08); + color: var(--color_bg_layer); + font-size: .875rem; + font-weight: 700; + line-height: 1.375rem; + font-weight: 500; + letter-spacing: -.2px; + transition: all .3s ease-in-out; + + &:hover { + background: var(--color_fg_on_button_primary); + box-shadow: 0 0 0 4px rgba(0,0,0,.2); + transition: all .2s ease-out; + } :global(html[lang="ja"]) & { @include LocaleLineHeight("ja", 1rem); @@ -203,6 +286,6 @@ svg { height: 0.75rem; width: 0.75rem; - fill: $neutrals-light-100-snow; + fill: var(--color_bg_layer); } } diff --git a/src/features/common/components/errors/error-page/error-page.component.tsx b/src/features/common/components/errors/error-page/error-page.component.tsx index 35d32065..96f0f8c5 100644 --- a/src/features/common/components/errors/error-page/error-page.component.tsx +++ b/src/features/common/components/errors/error-page/error-page.component.tsx @@ -1,29 +1,24 @@ import React from "react"; import styles from "./error-page.module.scss"; import { ShellComponent } from "@/features/common/components/shell/shell.component"; -import { RibbonComponent } from "@/features/common/components/bars/ribbon/ribbon.component"; import { MobileHeaderComponent } from "@/features/common/components/headers/mobile-header/mobile-header.component"; import { HeaderComponent } from "@/features/common/components/headers/header/header.component"; import { FooterComponent } from "@/features/common/components/footer/footer.component"; -import { LayoutDictionaryModel } from "@/features/localization/models/layout-dictionary.model"; import { ThemeCookieValues } from "@/features/common/values/theme.values"; -import { getImageDictionary } from "@/features/localization/services/images-dictionary.service"; -import { SiteLogoComponent } from "@/features/common/components/site-logo/site-logo.component"; +import { getLayoutDictionary } from "@/features/localization/services/language-dictionary.service"; interface ErrorPageComponentProps { languageCode: string; themeCode: ThemeCookieValues; - dictionary: LayoutDictionaryModel; children: React.ReactNode; } export const ErrorPageComponent: React.FC = ({ languageCode, themeCode, - dictionary, children, }) => { - const images = getImageDictionary(languageCode); + const layoutDictionary = getLayoutDictionary(languageCode); return ( @@ -31,35 +26,19 @@ export const ErrorPageComponent: React.FC = ({
} - ribbon={ - - } + dictionary={layoutDictionary} + themeCode={themeCode} /> } - ribbon={ - - } + dictionary={layoutDictionary} + themeCode={themeCode} />
{children}
} + dictionary={layoutDictionary.footer} /> diff --git a/src/features/common/components/footer/footer.component.tsx b/src/features/common/components/footer/footer.component.tsx index 42859a07..3e9277ae 100644 --- a/src/features/common/components/footer/footer.component.tsx +++ b/src/features/common/components/footer/footer.component.tsx @@ -1,6 +1,12 @@ "use client"; -import React, { MouseEvent, useState } from "react"; +import React, { MouseEvent, useEffect, useState } from "react"; +import Select, { + SingleValue, + OptionsOrGroups, + GroupBase, + NonceProvider, +} from "react-select"; import { FooterIconsComponent } from "./footer-Icons.component"; import { MonoFont, SecondaryFont } from "@/libs/theme/fonts"; import Image from "next/image"; @@ -15,25 +21,43 @@ import { DEFAULT_LANGUAGE_CODE } from "@/features/localization/localization.conf import { sitePaths } from "@/features/seo/site-tree"; import { createUrlPath } from "@/libs/utils/path.utils"; import { SiteBrandComponent } from "@/features/common/components/site-brand/site-brand.component"; -import { StaticImageMetadataModel } from "@/features/common/models/static-image-metadata.model"; import { Button } from "react-aria-components"; +import { Auth0LogoComponent } from "../../assets/auth0-logo.component"; +import { getBrandDictionary } from "@/features/localization/services/brand-dictionary.service"; +import { savePreferredLanguage } from "@/features/localization/services/ui-language.utils"; +import { UiLanguageModel } from "../../models/ui-language.model"; +import { GlobeIconComponent } from "../bars/ribbon/assets/globe-icon.component"; interface FooterComponentProps { languageCode: string; dictionary: LayoutDictionaryModel["footer"]; - auth0Logo: StaticImageMetadataModel; - siteLogo: React.ReactNode; } export const FooterComponent: React.FC = ({ languageCode, dictionary, - auth0Logo, - siteLogo, }) => { + const currentLanguage = dictionary.languagePicker.options.filter( + (element) => element.value === languageCode + )[0]; + + const handleChange = (selection: SingleValue) => { + if (!selection) { + return; + } + savePreferredLanguage(selection.value); + }; + const [modalState, setModalState] = useState( - ModalStateValues.CLOSED, + ModalStateValues.CLOSED ); + const [portalTarget, setPortalTarget] = useState(null); + + useEffect(() => { + setPortalTarget(document.body); + }, []); + + const images = getBrandDictionary(languageCode); const languagePathPrefix: string = languageCode === DEFAULT_LANGUAGE_CODE @@ -66,9 +90,10 @@ export const FooterComponent: React.FC = ({ contentClassName={styles.content} >
- - {siteLogo} - +
@@ -114,7 +139,7 @@ export const FooterComponent: React.FC = ({ }} className={clsx( styles.resource__button, - SecondaryFont.className, + SecondaryFont.className )} > {trigger.text} @@ -162,21 +187,80 @@ export const FooterComponent: React.FC = ({ target="_blank" href="https://auth0.com/" > - {auth0Logo.alt} + {dictionary.copyright} + {dictionary.languagePicker.options.length > 1 && ( +
+ +