diff --git a/.github/workflows/check-arch-package.yaml b/.github/workflows/check-arch-package.yaml index b639dd8f0..5fef42926 100644 --- a/.github/workflows/check-arch-package.yaml +++ b/.github/workflows/check-arch-package.yaml @@ -1,10 +1,10 @@ on: pull_request: paths: - - 'arch/**' + - "arch/**" push: paths: - - 'arch/**' + - "arch/**" workflow_dispatch: jobs: diff --git a/crates/radicle-tauri/capabilities/default.json b/crates/radicle-tauri/capabilities/default.json index c00a2be8d..d5a6e2cbf 100644 --- a/crates/radicle-tauri/capabilities/default.json +++ b/crates/radicle-tauri/capabilities/default.json @@ -15,6 +15,7 @@ "core:tray:default", "core:webview:default", "core:window:allow-set-badge-count", + "core:window:allow-start-dragging", "core:window:default", "dialog:default", "log:default", diff --git a/crates/radicle-tauri/tauri.conf.json b/crates/radicle-tauri/tauri.conf.json index cf4055c94..8be2f50e2 100644 --- a/crates/radicle-tauri/tauri.conf.json +++ b/crates/radicle-tauri/tauri.conf.json @@ -14,7 +14,9 @@ { "title": "Radicle", "minWidth": 960, - "minHeight": 600 + "minHeight": 600, + "titleBarStyle": "Overlay", + "hiddenTitle": true } ], "security": { diff --git a/crates/radicle-types/Cargo.toml b/crates/radicle-types/Cargo.toml index 775a8ee3e..dbddc8b15 100644 --- a/crates/radicle-types/Cargo.toml +++ b/crates/radicle-types/Cargo.toml @@ -3,6 +3,9 @@ name = "radicle-types" version = "0.1.0" edition = "2021" +[lib] +doctest = false + [dependencies] anyhow = { version = "1.0.90" } axum = { version = "0.8.1", default-features = false, features = ["json"] } diff --git a/crates/test-http-api/Cargo.toml b/crates/test-http-api/Cargo.toml index 5b3654f68..1c84dba63 100644 --- a/crates/test-http-api/Cargo.toml +++ b/crates/test-http-api/Cargo.toml @@ -5,6 +5,9 @@ homepage = "https://radicle.xyz" version = "0.1.0" edition = "2021" +[lib] +doctest = false + [dependencies] anyhow = { version = "1.0.90" } axum = { version = "0.8.1", default-features = false, features = ["json", "query", "tokio", "http1"] } diff --git a/index.html b/index.html index 89e6b13bc..2ef36a0f2 100644 --- a/index.html +++ b/index.html @@ -3,9 +3,17 @@ @@ -13,25 +21,19 @@ Radicle - diff --git a/package-lock.json b/package-lock.json index 5e79a13dc..7196c13ee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "GPL-3.0-only", "dependencies": { + "@floating-ui/dom": "^1.7.6", "@tauri-apps/api": "^2.5.0", "@tauri-apps/plugin-clipboard-manager": "^2.2.2", "@tauri-apps/plugin-dialog": "^2.2.1", @@ -633,6 +634,31 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", diff --git a/package.json b/package.json index fc5791905..3a53128a4 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "license": "GPL-3.0-only", "dependencies": { + "@floating-ui/dom": "^1.7.6", "@tauri-apps/api": "^2.5.0", "@tauri-apps/plugin-clipboard-manager": "^2.2.2", "@tauri-apps/plugin-dialog": "^2.2.1", diff --git a/public/colors.css b/public/colors.css index 2c50f8dd0..9400314cf 100644 --- a/public/colors.css +++ b/public/colors.css @@ -1,133 +1,313 @@ +/* Light theme. */ :root { - --color-background-default: #ebebff; - --color-background-float: #f5f5ff; - --color-background-dip: #dbdbff; - --color-foreground-contrast: #232563; - --color-foreground-dim: #5c5c70; - --color-foreground-emphasized: #7070ff; - --color-foreground-emphasized-hover: #7070ff; - --color-foreground-match-background: #ebebff; - --color-foreground-white: #ffffff; - --color-foreground-black: #000000; - --color-foreground-primary: #ff55ff; - --color-foreground-primary-hover: #ff8fff; - --color-foreground-success: #4fa877; - --color-foreground-red: #aa5078; - --color-foreground-yellow: #b29401; - --color-foreground-disabled: #9b9bb1; - --color-border-hint: #dbdbff; - --color-border-default: #ccceff; - --color-border-focus: #7070ff; - --color-border-contrast: #25262d; - --color-border-error: #ce97af; - --color-border-merged: #ffe5ff; - --color-border-match-background: #ebebff; - --color-border-primary: #ff1aff; - --color-border-primary-hover: #ff55ff; - --color-border-selected: #dbdbff; - --color-border-warning: #ffe609; - --color-border-success: #97ceb0; - --color-fill-secondary: #5555ff; - --color-fill-secondary-hover: #7070ff; - --color-fill-ghost: #dbdbff; - --color-fill-ghost-hover: #ebebff; - --color-fill-separator: #dbdbff; - --color-fill-primary: #ff55ff; - --color-fill-primary-hover: #ff70ff; - --color-fill-primary-shade: #ff1aff; - --color-fill-danger-shade: #8b3e5e; - --color-fill-danger: #aa5078; - --color-fill-danger-hover: #be7495; - --color-fill-danger-counter: #ce97af; - --color-fill-yellow: #ffe609; - --color-fill-yellow-iconic: #ffff55; - --color-fill-gray: #9b9bb1; - --color-fill-secondary-shade: #4545ef; - --color-fill-diff-red: #efdce4; - --color-fill-diff-red-light: #f7eef2; - --color-fill-success-counter: #97ceb0; - --color-fill-success-hover: #71bc93; - --color-fill-success: #4fa877; - --color-fill-success-shade: #408760; - --color-fill-diff-green: #badeca; - --color-fill-diff-green-light: #dcefe5; - --color-fill-float: #f5f5ff; - --color-fill-float-hover: #fafaff; - --color-fill-merged: #ffeeff; - --color-fill-selected: #ebebff; - --color-fill-warning: #ffffe5; - --color-fill-counter: #dbdbff; - --color-fill-counter-emphasized: #ebebff; - --color-fill-delegate: #ffe5ff; - --color-fill-private: #fff5d6; - --color-fill-secondary-counter: #b2b5ff; - --color-fill-primary-counter: #ff8fff; - --color-fill-ghost-shade: #ccceff; + --color-surface-base: var(--color-neutrals-opaque-light-50); + --color-surface-canvas: var(--color-neutrals-opaque-light-0); + --color-surface-subtle: var(--color-neutrals-opaque-light-100); + --color-surface-mid: var(--color-neutrals-opaque-light-150); + --color-surface-strong: var(--color-neutrals-opaque-light-200); + --color-surface-alpha-subtle: var(--color-neutrals-alpha-light-50); + --color-surface-alpha-mid: var(--color-neutrals-alpha-light-100); + --color-surface-alpha-strong: var(--color-neutrals-alpha-light-200); + --color-surface-scrim: var(--color-neutrals-alpha-light-500); + --color-surface-open: var(--color-accent-green-200); + --color-surface-merged: var(--color-accent-blue-200); + --color-surface-draft: var(--color-neutrals-opaque-light-150); + --color-surface-closed: var(--color-accent-blue-200); + --color-surface-archived: var(--color-accent-pink-200); + + --color-text-primary: var(--color-neutrals-opaque-light-900); + --color-text-secondary: var(--color-neutrals-opaque-light-700); + --color-text-tertiary: var(--color-neutrals-opaque-light-600); + --color-text-quaternary: var(--color-neutrals-opaque-light-500); + --color-text-disabled: var(--color-neutrals-alpha-light-400); + --color-text-open: var(--color-accent-green-800); + --color-text-merged: var(--color-accent-blue-800); + --color-text-draft: var(--color-neutrals-opaque-light-600); + --color-text-closed: var(--color-accent-blue-800); + --color-text-archived: var(--color-accent-pink-800); + + --color-border-subtle: var(--color-neutrals-opaque-light-150); + --color-border-mid: var(--color-neutrals-opaque-light-200); + --color-border-strong: var(--color-neutrals-opaque-light-300); + --color-border-alpha-subtle: var(--color-neutrals-alpha-light-100); + --color-border-alpha-mid: var(--color-neutrals-alpha-light-200); + + --color-feedback-success-text: var(--color-semantic-green-800); + --color-feedback-success-border: var(--color-semantic-green-600); + --color-feedback-success-bg: var(--color-semantic-green-100); + --color-feedback-success-bg-selected: var(--color-semantic-green-200); + --color-feedback-warning-text: var(--color-semantic-amber-800); + --color-feedback-warning-border: var(--color-semantic-amber-600); + --color-feedback-warning-bg: var(--color-semantic-amber-100); + --color-feedback-error-text: var(--color-semantic-red-800); + --color-feedback-error-border: var(--color-semantic-red-600); + --color-feedback-error-bg: var(--color-semantic-red-100); + --color-feedback-error-bg-selected: var(--color-semantic-red-200); + + --color-feedback-success-fill: var(--color-semantic-green-600); + --color-feedback-success-fill-hover: var(--color-semantic-green-500); + --color-feedback-success-fill-active: var(--color-semantic-green-700); + --color-feedback-error-fill: var(--color-semantic-red-600); + --color-feedback-error-fill-hover: var(--color-semantic-red-500); + --color-feedback-error-fill-active: var(--color-semantic-red-700); + + --color-code-keywords: var(--color-accent-blue-700); + --color-code-strings: var(--color-accent-green-700); + --color-code-numbers: var(--color-accent-purple-700); + --color-code-comments: var(--color-neutrals-opaque-light-700); + --color-code-error: var(--color-semantic-red-700); + --color-code-functions: var(--color-accent-emerald-700); + + /* Brand. */ + --color-brand-bg: var(--color-accent-blue-600); + --color-brand-hover: var(--color-accent-blue-500); + --color-brand-text-light: var(--color-accent-blue-600); + --color-brand-text-dark: var(--color-accent-blue-400); + --color-text-on-brand: var(--color-neutrals-opaque-light-0); + + --color-surface-brand-primary: var(--color-brand-bg); + --color-surface-brand-secondary: var(--color-brand-hover); + --color-border-brand: var(--color-brand-hover); + --color-text-brand: var(--color-brand-text-light); } +/* Dark theme. */ :root[data-theme="dark"] { - --color-background-default: #0a0e0f; - --color-background-float: #25262d; - --color-background-dip: #000000; - --color-foreground-contrast: #f9f9fb; - --color-foreground-dim: #9b9bb1; - --color-foreground-emphasized: #7070ff; - --color-foreground-emphasized-hover: #b2b5ff; - --color-foreground-match-background: #0a0e0f; - --color-foreground-white: #ffffff; - --color-foreground-black: #000000; - --color-foreground-primary: #ff55ff; - --color-foreground-primary-hover: #ff8fff; - --color-foreground-success: #4fa877; - --color-foreground-red: #be7495; - --color-foreground-yellow: #e5c001; - --color-foreground-disabled: #5c5c70; - --color-border-hint: #2e2f38; - --color-border-default: #393a46; - --color-border-focus: #7070ff; - --color-border-contrast: #ebebff; - --color-border-error: #6b2b42; - --color-border-merged: #6b006b; - --color-border-match-background: #0a0e0f; - --color-border-primary: #ff1aff; - --color-border-primary-hover: #ff55ff; - --color-border-selected: #232563; - --color-border-warning: #4c4000; - --color-border-success: #2a5a40; - --color-fill-secondary: #7070ff; - --color-fill-secondary-hover: #b2b5ff; - --color-fill-secondary-shade: #5555ff; - --color-fill-ghost: #2e2f38; - --color-fill-ghost-hover: #393a46; - --color-fill-separator: #2e2f38; - --color-fill-primary: #ff1aff; - --color-fill-primary-hover: #ff4dff; - --color-fill-primary-shade: #e500e5; - --color-fill-danger-shade: #8b3e5e; - --color-fill-danger: #aa5078; - --color-fill-danger-hover: #be7495; - --color-fill-danger-counter: #ce97af; - --color-fill-yellow: #ffe609; - --color-fill-yellow-iconic: #ffff55; - --color-fill-gray: #9b9bb1; - --color-fill-diff-red: #4d1929; - --color-fill-diff-red-light: #2d060d; - --color-fill-success-counter: #97ceb0; - --color-fill-success-hover: #71bc93; - --color-fill-success: #4fa877; - --color-fill-success-shade: #408760; - --color-fill-diff-green: #183425; - --color-fill-diff-green-light: #142a1d; - --color-fill-float: #25262d; - --color-fill-float-hover: #2e2f38; - --color-fill-merged: #1a001a; - --color-fill-selected: #16173d; - --color-fill-warning: #191500; - --color-fill-counter: #393a46; - --color-fill-counter-emphasized: #5c5c70; - --color-fill-delegate: #3d003d; - --color-fill-private: #4c4000; - --color-fill-secondary-counter: #ccceff; - --color-fill-primary-counter: #ff8fff; - --color-fill-ghost-shade: #25262d; + --color-surface-base: var(--color-neutrals-opaque-dark-0); + --color-surface-canvas: var(--color-neutrals-opaque-dark-50); + --color-surface-subtle: var(--color-neutrals-opaque-dark-100); + --color-surface-mid: var(--color-neutrals-opaque-dark-150); + --color-surface-strong: var(--color-neutrals-opaque-dark-200); + --color-surface-alpha-subtle: var(--color-neutrals-alpha-dark-50); + --color-surface-alpha-mid: var(--color-neutrals-alpha-dark-100); + --color-surface-alpha-strong: var(--color-neutrals-alpha-dark-200); + --color-surface-scrim: var(--color-neutrals-alpha-light-700); + --color-surface-open: var(--color-accent-green-900); + --color-surface-draft: var(--color-neutrals-opaque-dark-150); + --color-surface-closed: var(--color-accent-blue-900); + --color-surface-merged: var(--color-accent-blue-900); + --color-surface-archived: var(--color-accent-pink-900); + + --color-text-primary: var(--color-neutrals-opaque-dark-900); + --color-text-secondary: var(--color-neutrals-opaque-dark-700); + --color-text-tertiary: var(--color-neutrals-opaque-dark-600); + --color-text-quaternary: var(--color-neutrals-opaque-dark-500); + --color-text-disabled: var(--color-neutrals-alpha-dark-400); + --color-text-open: var(--color-accent-green-500); + --color-text-draft: var(--color-neutrals-opaque-dark-600); + --color-text-closed: var(--color-accent-blue-500); + --color-text-merged: var(--color-accent-blue-500); + --color-text-archived: var(--color-accent-pink-500); + + --color-border-subtle: var(--color-neutrals-opaque-dark-150); + --color-border-mid: var(--color-neutrals-opaque-dark-200); + --color-border-strong: var(--color-neutrals-opaque-dark-300); + --color-border-alpha-subtle: var(--color-neutrals-alpha-dark-100); + --color-border-alpha-mid: var(--color-neutrals-alpha-dark-200); + + --color-feedback-success-text: var(--color-semantic-green-400); + --color-feedback-success-border: var(--color-semantic-green-700); + --color-feedback-success-bg: var(--color-semantic-green-900); + --color-feedback-success-bg-selected: var(--color-semantic-green-800); + --color-feedback-warning-text: var(--color-semantic-amber-400); + --color-feedback-warning-border: var(--color-semantic-amber-700); + --color-feedback-warning-bg: var(--color-semantic-amber-900); + --color-feedback-error-text: var(--color-semantic-red-400); + --color-feedback-error-border: var(--color-semantic-red-700); + --color-feedback-error-bg: var(--color-semantic-red-900); + --color-feedback-error-bg-selected: var(--color-semantic-red-800); + + --color-code-keywords: var(--color-accent-blue-400); + --color-code-strings: var(--color-accent-green-400); + --color-code-numbers: var(--color-accent-purple-400); + --color-code-comments: var(--color-neutrals-opaque-dark-700); + --color-code-error: var(--color-semantic-red-400); + --color-code-functions: var(--color-accent-emerald-400); + + --color-text-brand: var(--color-brand-text-dark); +} + +/* Internal color system, don't use directly! */ +:root { + --color-neutrals-opaque-light-0: #ffffff; + --color-neutrals-opaque-light-50: #f8f9fa; + --color-neutrals-opaque-light-100: #f1f3f5; + --color-neutrals-opaque-light-150: #e9ebef; + --color-neutrals-opaque-light-200: #e1e4e8; + --color-neutrals-opaque-light-250: #d6d9e0; + --color-neutrals-opaque-light-300: #c9cdd4; + --color-neutrals-opaque-light-400: #a2a7b1; + --color-neutrals-opaque-light-500: #7a8190; + --color-neutrals-opaque-light-600: #5a5f6b; + --color-neutrals-opaque-light-700: #3a3f49; + --color-neutrals-opaque-light-800: #1f232b; + --color-neutrals-opaque-light-900: #0b0d12; + + --color-neutrals-opaque-dark-0: #00060f; + --color-neutrals-opaque-dark-50: #0a1018; + --color-neutrals-opaque-dark-100: #141a22; + --color-neutrals-opaque-dark-150: #1f242c; + --color-neutrals-opaque-dark-200: #29303a; + --color-neutrals-opaque-dark-250: #33383f; + --color-neutrals-opaque-dark-300: #3d4248; + --color-neutrals-opaque-dark-400: #52565c; + --color-neutrals-opaque-dark-500: #666a6f; + --color-neutrals-opaque-dark-600: #8f9296; + --color-neutrals-opaque-dark-700: #b8babc; + --color-neutrals-opaque-dark-800: #e0e1e2; + --color-neutrals-opaque-dark-900: #ffffff; + + --color-neutrals-alpha-light-50: #00060f0a; + --color-neutrals-alpha-light-100: #00060f14; + --color-neutrals-alpha-light-200: #00060f1f; + --color-neutrals-alpha-light-300: #00060f29; + --color-neutrals-alpha-light-400: #00060f3d; + --color-neutrals-alpha-light-500: #00060f52; + --color-neutrals-alpha-light-600: #00060f7a; + --color-neutrals-alpha-light-700: #00060fa3; + --color-neutrals-alpha-light-800: #00060fcc; + --color-neutrals-alpha-light-900: #00060feb; + + --color-neutrals-alpha-dark-50: #ffffff0a; + --color-neutrals-alpha-dark-100: #ffffff14; + --color-neutrals-alpha-dark-200: #ffffff1f; + --color-neutrals-alpha-dark-300: #ffffff29; + --color-neutrals-alpha-dark-400: #ffffff3d; + --color-neutrals-alpha-dark-500: #ffffff52; + --color-neutrals-alpha-dark-600: #ffffff7a; + --color-neutrals-alpha-dark-700: #ffffffa3; + --color-neutrals-alpha-dark-800: #ffffffcc; + --color-neutrals-alpha-dark-900: #ffffffeb; + + --color-accent-blue-0: #f4f9ff; + --color-accent-blue-100: #d6e9ff; + --color-accent-blue-200: #afcfff; + --color-accent-blue-300: #7fb0ff; + --color-accent-blue-400: #4d94ff; + --color-accent-blue-500: #1c77ff; + --color-accent-blue-600: #165fcc; + --color-accent-blue-700: #104799; + --color-accent-blue-800: #0b3266; + --color-accent-blue-900: #061d33; + + --color-accent-green-0: #f7fff2; + --color-accent-green-100: #dffcc6; + --color-accent-green-200: #bef98a; + --color-accent-green-300: #99f24c; + --color-accent-green-400: #73e926; + --color-accent-green-500: #58e600; + --color-accent-green-600: #46ba00; + --color-accent-green-700: #358f00; + --color-accent-green-800: #256400; + --color-accent-green-900: #0a1f00; + + --color-accent-cyan-0: #f1fefe; + --color-accent-cyan-100: #ccf9fa; + --color-accent-cyan-200: #99eff0; + --color-accent-cyan-300: #66e5e7; + --color-accent-cyan-400: #33dbde; + --color-accent-cyan-500: #00d4da; + --color-accent-cyan-600: #00a8ae; + --color-accent-cyan-700: #007d82; + --color-accent-cyan-800: #005256; + --color-accent-cyan-900: #00292b; + + --color-accent-purple-0: #f7f5ff; + --color-accent-purple-100: #e0daff; + --color-accent-purple-200: #c0b5ff; + --color-accent-purple-300: #a08fff; + --color-accent-purple-400: #8a74fa; + --color-accent-purple-500: #886bf2; + --color-accent-purple-600: #6c54c2; + --color-accent-purple-700: #503f91; + --color-accent-purple-800: #352a61; + --color-accent-purple-900: #1b1530; + + --color-accent-pink-0: #fff6ff; + --color-accent-pink-100: #ffedff; + --color-accent-pink-200: #ffdbff; + --color-accent-pink-300: #ffc9ff; + --color-accent-pink-400: #ffb7ff; + --color-accent-pink-500: #ffa5ff; + --color-accent-pink-600: #cc84cc; + --color-accent-pink-700: #996399; + --color-accent-pink-800: #664266; + --color-accent-pink-900: #332133; + + --color-accent-emerald-0: #f2fcf9; + --color-accent-emerald-100: #ccf4e4; + --color-accent-emerald-200: #99e9ca; + --color-accent-emerald-300: #66ddb0; + --color-accent-emerald-400: #33d196; + --color-accent-emerald-500: #009f67; + --color-accent-emerald-600: #007f52; + --color-accent-emerald-700: #005f3e; + --color-accent-emerald-800: #004029; + --color-accent-emerald-900: #002015; + + --color-accent-citrus-0: #fcfff2; + --color-accent-citrus-100: #f1ffbf; + --color-accent-citrus-200: #e4ff80; + --color-accent-citrus-300: #d6ff40; + --color-accent-citrus-400: #caff20; + --color-accent-citrus-500: #ccff38; + --color-accent-citrus-600: #a4cc2d; + --color-accent-citrus-700: #7c991f; + --color-accent-citrus-800: #536613; + --color-accent-citrus-900: #2b3307; + + --color-accent-olive-0: #fafaed; + --color-accent-olive-100: #e6e5ba; + --color-accent-olive-200: #d1cf86; + --color-accent-olive-300: #bdb950; + --color-accent-olive-400: #9e9900; + --color-accent-olive-500: #585600; + --color-accent-olive-600: #464400; + --color-accent-olive-700: #343300; + --color-accent-olive-800: #232200; + --color-accent-olive-900: #111100; + + --color-semantic-red-0: #fff5f5; + --color-semantic-red-100: #ffd6d6; + --color-semantic-red-200: #ffafaf; + --color-semantic-red-300: #ff8686; + --color-semantic-red-400: #ff5c5c; + --color-semantic-red-500: #ff4d4f; + --color-semantic-red-600: #cc3e3f; + --color-semantic-red-700: #992f2f; + --color-semantic-red-800: #661f20; + --color-semantic-red-900: #330f10; + + --color-semantic-amber-0: #fffdf2; + --color-semantic-amber-100: #fff1b8; + --color-semantic-amber-200: #ffe58f; + --color-semantic-amber-300: #ffd666; + --color-semantic-amber-400: #ffc53d; + --color-semantic-amber-500: #faad14; + --color-semantic-amber-600: #d48806; + --color-semantic-amber-700: #ad6800; + --color-semantic-amber-800: #874d00; + --color-semantic-amber-900: #613400; + + --color-semantic-green-0: #f6fff1; + --color-semantic-green-100: #d9f7be; + --color-semantic-green-200: #b7eb8f; + --color-semantic-green-300: #95de64; + --color-semantic-green-400: #73d13d; + --color-semantic-green-500: #52c41a; + --color-semantic-green-600: #3d9914; + --color-semantic-green-700: #2e7010; + --color-semantic-green-800: #1e470a; + --color-semantic-green-900: #0f2305; + + --color-semantic-blue-0: #f0f9ff; + --color-semantic-blue-100: #c6e4ff; + --color-semantic-blue-200: #91caff; + --color-semantic-blue-300: #5bafff; + --color-semantic-blue-400: #2896ff; + --color-semantic-blue-500: #1890ff; + --color-semantic-blue-600: #1373cc; + --color-semantic-blue-700: #0e5799; + --color-semantic-blue-800: #093c66; + --color-semantic-blue-900: #051f33; } diff --git a/public/flower.png b/public/flower.png new file mode 100644 index 000000000..0618beea5 Binary files /dev/null and b/public/flower.png differ diff --git a/public/fonts/Booton-Medium.woff2 b/public/fonts/Booton-Medium.woff2 new file mode 100755 index 000000000..5f5bfcc56 Binary files /dev/null and b/public/fonts/Booton-Medium.woff2 differ diff --git a/public/fonts/Booton-Regular.woff2 b/public/fonts/Booton-Regular.woff2 new file mode 100755 index 000000000..fac416934 Binary files /dev/null and b/public/fonts/Booton-Regular.woff2 differ diff --git a/public/fonts/Booton-SemiBold.woff2 b/public/fonts/Booton-SemiBold.woff2 new file mode 100755 index 000000000..1c8ed8fe6 Binary files /dev/null and b/public/fonts/Booton-SemiBold.woff2 differ diff --git a/public/fonts/Inter-Bold.woff2 b/public/fonts/Inter-Bold.woff2 deleted file mode 100644 index 2846f29cc..000000000 Binary files a/public/fonts/Inter-Bold.woff2 and /dev/null differ diff --git a/public/fonts/Inter-Medium.woff2 b/public/fonts/Inter-Medium.woff2 deleted file mode 100644 index f92498a2e..000000000 Binary files a/public/fonts/Inter-Medium.woff2 and /dev/null differ diff --git a/public/fonts/Inter-Regular.woff2 b/public/fonts/Inter-Regular.woff2 deleted file mode 100644 index 6c2b6893d..000000000 Binary files a/public/fonts/Inter-Regular.woff2 and /dev/null differ diff --git a/public/fonts/Inter-SemiBold.woff2 b/public/fonts/Inter-SemiBold.woff2 deleted file mode 100644 index 611e90c95..000000000 Binary files a/public/fonts/Inter-SemiBold.woff2 and /dev/null differ diff --git a/public/index.css b/public/index.css index 56b4b9f14..4de5159d1 100644 --- a/public/index.css +++ b/public/index.css @@ -2,6 +2,14 @@ box-sizing: border-box; } +:root { + --border-radius-xs: 0.0625rem; + --border-radius-sm: 0.125rem; + --border-radius-md: 0.25rem; + --border-radius-lg: 0.5rem; + --border-radius-full: 99rem; +} + html { height: 100%; width: 100%; @@ -18,8 +26,8 @@ body { width: 100%; margin: 0; padding: 0; - color: var(--color-foreground-contrast); - background-color: var(--color-background-default); + color: var(--color-text-primary); + background-color: var(--color-surface-base); } a, @@ -44,28 +52,8 @@ a { } ::selection { - background: var(--color-fill-yellow); - color: var(--color-foreground-black); -} - -.global-oid { - color: var(--color-foreground-emphasized); - font-size: var(--font-size-small); - font-family: var(--font-family-monospace); - font-weight: var(--font-weight-regular); -} - -.global-commit { - color: var(--color-foreground-dim); - font-size: var(--font-size-small); - font-family: var(--font-family-monospace); - font-weight: var(--font-weight-semibold); -} - -.global-none { - color: var(--color-foreground-disabled); - font-weight: var(--font-weight-bold); - font-family: var(--font-family-monospace); + background: var(--color-feedback-warning-border); + color: var(--color-text-primary); } .global-flex { @@ -76,7 +64,7 @@ a { .global-tab { padding-left: 0.5rem; - border-left: 1px solid var(--color-fill-ghost-hover); + border-left: 1px solid var(--color-border-mid); margin-left: 1rem; flex-direction: column; justify-content: space-between; @@ -84,21 +72,35 @@ a { column-gap: 0.5rem; } -.global-counter { +.global-chip { display: flex; align-items: center; justify-content: center; - background-color: var(--color-fill-counter); - clip-path: var(--1px-corner-fill); + background-color: var(--color-surface-strong); + border-radius: var(--border-radius-sm); height: 1.5rem; padding: 0 0.5rem; min-width: 1.5rem; - font-weight: var(--font-weight-regular); + font: var(--txt-body-s-regular); + flex-shrink: 0; +} + +.global-counter-badge { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.25rem; + min-width: 1.25rem; + padding: 0.125rem 0.25rem; + border-radius: 1px; + background-color: var(--color-surface-alpha-subtle); + color: var(--color-text-secondary); + font: var(--txt-body-s-regular); flex-shrink: 0; } .global-link { - color: var(--color-foreground-default); + color: var(--color-text-secondary); text-decoration: none; } .global-link:hover { @@ -113,9 +115,9 @@ a { --os-handle-border-radius: 4px; --os-padding-perpendicular: 4px; --os-padding-axis: 2px; - --os-handle-bg: var(--color-border-default); - --os-handle-bg-hover: var(--color-border-default); - --os-handle-bg-active: var(--color-border-default); + --os-handle-bg: var(--color-border-mid); + --os-handle-bg-hover: var(--color-border-mid); + --os-handle-bg-active: var(--color-border-mid); --os-track-bg: transparent; } } @@ -128,170 +130,6 @@ a { --elevation-low: 0 0 16px 0 #00000022; } -:root { - --1px-corner-fill: polygon( - 0 2px, - 2px 2px, - 2px 0, - calc(100% - 2px) 0, - calc(100% - 2px) 2px, - 100% 2px, - 100% calc(100% - 2px), - calc(100% - 2px) calc(100% - 2px), - calc(100% - 2px) calc(100% - 2px), - calc(100% - 2px) 100%, - 2px 100%, - 2px calc(100% - 2px), - 0 calc(100% - 2px) - ); - - --1px-top-corner-fill: polygon( - 0 2px, - 2px 2px, - 2px 0, - calc(100% - 2px) 0, - calc(100% - 2px) 2px, - 100% 2px, - 100% calc(100% - 2px), - 100% 4px, - 100% 100%, - 0 100% - ); - - --1px-bottom-corner-fill: polygon( - 0 0, - 100% 0, - 100% calc(100% - 2px), - calc(100% - 2px) calc(100% - 2px), - calc(100% - 2px) 100%, - 2px 100%, - 2px calc(100% - 2px), - 0 calc(100% - 2px) - ); - - --2px-corner-fill: polygon( - 0 4px, - 2px 4px, - 2px 2px, - 4px 2px, - 4px 0, - calc(100% - 4px) 0, - calc(100% - 4px) 2px, - calc(100% - 2px) 2px, - calc(100% - 2px) 4px, - 100% 4px, - 100% calc(100% - 4px), - calc(100% - 2px) calc(100% - 4px), - calc(100% - 2px) calc(100% - 2px), - calc(100% - 4px) calc(100% - 2px), - calc(100% - 4px) 100%, - 4px 100%, - 4px calc(100% - 2px), - 2px calc(100% - 2px), - 2px calc(100% - 4px), - 0 calc(100% - 4px) - ); - - --2px-top-corner-fill: polygon( - 0 4px, - 2px 4px, - 2px 2px, - 4px 2px, - 4px 0, - calc(100% - 4px) 0, - calc(100% - 4px) 2px, - calc(100% - 2px) 2px, - calc(100% - 2px) 4px, - 100% 4px, - 100% 100%, - 0 100% - ); - - --2px-bottom-corner-fill: polygon( - 0 0, - 100% 0, - 100% calc(100% - 4px), - calc(100% - 2px) calc(100% - 4px), - calc(100% - 2px) calc(100% - 2px), - calc(100% - 4px) calc(100% - 2px), - calc(100% - 4px) 100%, - 4px 100%, - 4px calc(100% - 2px), - 2px calc(100% - 2px), - 2px calc(100% - 4px), - 0 calc(100% - 4px) - ); - - --3px-corner-fill: polygon( - 0 6px, - 2px 6px, - 2px 4px, - 4px 4px, - 4px 2px, - 6px 2px, - 6px 0, - calc(100% - 6px) 0, - calc(100% - 6px) 2px, - calc(100% - 4px) 2px, - calc(100% - 4px) 4px, - calc(100% - 2px) 4px, - calc(100% - 2px) 6px, - 100% 6px, - 100% calc(100% - 6px), - calc(100% - 2px) calc(100% - 6px), - calc(100% - 2px) calc(100% - 4px), - calc(100% - 4px) calc(100% - 4px), - calc(100% - 4px) calc(100% - 2px), - calc(100% - 6px) calc(100% - 2px), - calc(100% - 6px) 100%, - 6px 100%, - 6px calc(100% - 2px), - 4px calc(100% - 2px), - 4px calc(100% - 4px), - 2px calc(100% - 4px), - 2px calc(100% - 6px), - 0 calc(100% - 6px) - ); - - --3px-top-corner-fill: polygon( - 0 6px, - 2px 6px, - 2px 4px, - 4px 4px, - 4px 2px, - 6px 2px, - 6px 0, - calc(100% - 6px) 0, - calc(100% - 6px) 2px, - calc(100% - 4px) 2px, - calc(100% - 4px) 4px, - calc(100% - 2px) 4px, - calc(100% - 2px) 6px, - 100% 6px, - 100% 100%, - 0 100% - ); - - --3px-bottom-corner-fill: polygon( - 0 0, - 100% 0, - 100% calc(100% - 6px), - calc(100% - 2px) calc(100% - 6px), - calc(100% - 2px) calc(100% - 4px), - calc(100% - 4px) calc(100% - 4px), - calc(100% - 4px) calc(100% - 2px), - calc(100% - 6px) calc(100% - 2px), - calc(100% - 6px) 100%, - 6px 100%, - 6px calc(100% - 2px), - 4px calc(100% - 2px), - 4px calc(100% - 4px), - 2px calc(100% - 4px), - 2px calc(100% - 6px), - 0 calc(100% - 6px) - ); -} - /* Breakpoints =========== diff --git a/public/radicle.svg b/public/radicle.svg index 09b1015bd..add0c464d 100644 --- a/public/radicle.svg +++ b/public/radicle.svg @@ -1,63 +1,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/public/typography.css b/public/typography.css index 363edb1d3..1637b832a 100644 --- a/public/typography.css +++ b/public/typography.css @@ -1,30 +1,23 @@ @font-face { - font-family: "Inter"; + font-family: "Booton"; font-style: normal; font-weight: 400; font-display: swap; - src: url("fonts/Inter-Regular.woff2"); + src: url("fonts/Booton-Regular.woff2"); } @font-face { - font-family: "Inter"; - font-weight: 500; + font-family: "Booton"; + font-weight: 468; font-display: swap; - src: url("fonts/Inter-Medium.woff2"); + src: url("fonts/Booton-Medium.woff2"); } @font-face { - font-family: "Inter"; + font-family: "Booton"; font-weight: 600; font-display: swap; - src: url("fonts/Inter-SemiBold.woff2"); -} - -@font-face { - font-family: "Inter"; - font-weight: 700; - font-display: swap; - src: url("fonts/Inter-Bold.woff2"); + src: url("fonts/Booton-SemiBold.woff2"); } @font-face { @@ -59,94 +52,144 @@ :root { --font-size: 16px; - --font-family-sans-serif: Inter, sans-serif; - --font-family-monospace: monospace; - --font-weight-regular: 400; - --font-weight-medium: 500; - --font-weight-semibold: 600; - --font-weight-bold: 700; - --font-size-tiny: 0.75rem; /* 12px */ - --font-size-small: 0.875rem; /* 14px */ - --font-size-regular: 1rem; /* 16px */ - --font-size-medium: 1.25rem; /* 20px */ - --font-size-large: 1.5rem; /* 24px */ - --font-size-x-large: 2rem; /* 32px */ - --font-size-xx-large: 3rem; /* 48px */ + + --txt-heading-xxs: 600 0.75rem/1rem Booton, sans-serif; + --txt-heading-xs: 600 0.875rem/1.25rem Booton, sans-serif; + --txt-heading-s: 600 1rem/1.25rem Booton, sans-serif; + --txt-heading-m: 600 1.125rem/1.25rem Booton, sans-serif; + --txt-heading-l: 600 1.375rem/1.75rem Booton, sans-serif; + --txt-heading-xl: 600 1.75rem/2rem Booton, sans-serif; + --txt-heading-xxl: 600 2rem/2.25rem Booton, sans-serif; + + --txt-body-s-regular: 400 0.75rem/1rem Booton, sans-serif; + --txt-body-s-medium: 468 0.75rem/1rem Booton, sans-serif; + --txt-body-s-semibold: 600 0.75rem/1rem Booton, sans-serif; + + --txt-body-m-regular: 400 0.875rem/1.25rem Booton, sans-serif; + --txt-body-m-medium: 468 0.875rem/1.25rem Booton, sans-serif; + --txt-body-m-semibold: 600 0.875rem/1.25rem Booton, sans-serif; + + --txt-body-l-regular: 400 1rem/1.5rem Booton, sans-serif; + --txt-body-l-medium: 468 1rem/1.5rem Booton, sans-serif; + --txt-body-l-semibold: 600 1rem/1.5rem Booton, sans-serif; } [data-codefont="system"] { - --font-family-monospace: monospace; + --txt-code-regular: 400 0.875rem/1.25rem monospace; + --txt-code-semibold: 600 0.875rem/1.25rem monospace; } [data-codefont="jetbrains"] { - --font-family-monospace: "JetBrains Mono"; + --txt-code-regular: 400 0.875rem/1.25rem "JetBrains Mono"; + --txt-code-semibold: 600 0.875rem/1.25rem "JetBrains Mono"; } html { -ms-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; -webkit-text-size-adjust: 100%; - font-family: var(--font-family-sans-serif); - font-feature-settings: "zero"; + font-family: Booton, sans-serif; /* The root element font size has to be set in px, * otherwise Safari breaks. */ font-size: var(--font-size); - font-weight: var(--font-weight-regular); + font-weight: 400; + line-height: 1.25; } -body { - /* On Safari this aligns text with different font-faces properly vertically. */ - line-height: 1.375rem; +.txt-heading-xxs { + font: var(--txt-heading-xxs); } -p { - margin: 1rem 0; -} -.txt-tiny { - font-size: var(--font-size-tiny); -} -.txt-small { - font-size: var(--font-size-small); -} -.txt-regular { - font-size: var(--font-size-regular); -} -.txt-medium { - font-size: var(--font-size-medium); -} -.txt-large { - font-size: var(--font-size-large); -} -.txt-huge { - font-size: var(--font-size-x-large); -} -.txt-humongous { - font-size: var(--font-size-xx-large); +.txt-heading-xs { + font: var(--txt-heading-xs); } -.txt-monospace { - font-family: var(--font-family-monospace); +.txt-heading-s { + font: var(--txt-heading-s); } -.txt-bold { - font-weight: var(--font-weight-bold) !important; + +.txt-heading-m { + font: var(--txt-heading-m); } -.txt-semibold { - font-weight: var(--font-weight-semibold) !important; + +.txt-heading-l { + font: var(--txt-heading-l); } + +.txt-heading-xl { + font: var(--txt-heading-xl); +} + +.txt-heading-xxl { + font: var(--txt-heading-xxl); +} + +.txt-body-s-regular { + font: var(--txt-body-s-regular); +} + +.txt-body-s-medium { + font: var(--txt-body-s-medium); +} + +.txt-body-s-semibold { + font: var(--txt-body-s-semibold); +} + +.txt-body-m-regular { + font: var(--txt-body-m-regular); +} + +.txt-body-m-medium { + font: var(--txt-body-m-medium); +} + +.txt-body-m-semibold { + font: var(--txt-body-m-semibold); +} + +.txt-body-l-regular { + font: var(--txt-body-l-regular); +} + +.txt-body-l-medium { + font: var(--txt-body-l-medium); +} + +.txt-body-l-semibold { + font: var(--txt-body-l-semibold); +} + +.txt-code-regular { + font: var(--txt-code-regular); +} + +.txt-code-semibold { + font: var(--txt-code-semibold); +} + +.txt-id { + color: var(--color-text-tertiary); + font: var(--txt-code-regular); +} + .txt-missing { - color: var(--color-foreground-dim); + color: var(--color-text-secondary); } + .txt-selectable { -webkit-touch-callout: initial; -webkit-user-select: text; user-select: text; } + .txt-emoji { height: 1em; width: 1em; margin: 0 0.05em 0 0.1em; vertical-align: -0.1em; } + .txt-overflow { overflow: hidden; text-overflow: ellipsis; diff --git a/src/App.svelte b/src/App.svelte index d03ac3d1c..21231e80d 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -3,8 +3,10 @@ import type { ErrorWrapper } from "@bindings/error/ErrorWrapper"; import { listen } from "@tauri-apps/api/event"; + import { getCurrentWindow } from "@tauri-apps/api/window"; import delay from "lodash/delay"; import { onDestroy, onMount } from "svelte"; + import { get } from "svelte/store"; import { decreaseFontSize, @@ -16,6 +18,7 @@ import { nodeRunning } from "@app/lib/events"; import { dynamicInterval } from "@app/lib/interval"; import { invoke } from "@app/lib/invoke"; + import { hide } from "@app/lib/modal"; import * as router from "@app/lib/router"; import { setUnlistenNodeEvents, @@ -23,11 +26,16 @@ } from "@app/lib/startup.svelte"; import { isMac, unreachable } from "@app/lib/utils"; - import { theme } from "@app/components/ThemeSwitch.svelte"; - import Auth from "@app/views/booting/Auth.svelte"; - import CreateIdentity from "@app/views/booting/CreateIdentity.svelte"; - import Repos from "@app/views/home/Repos.svelte"; - import CreateIssue from "@app/views/repo/CreateIssue.svelte"; + import { codeFont } from "@app/components/CodeFontSwitch.svelte"; + import { + followSystemTheme, + loadTheme, + theme, + } from "@app/components/ThemeSwitch.svelte"; + import GuideView from "@app/modals/Guide.svelte"; + import Auth from "@app/views/auth/Auth.svelte"; + import CreateIdentity from "@app/views/auth/CreateIdentity.svelte"; + import InboxView from "@app/views/Inbox.svelte"; import Issue from "@app/views/repo/Issue.svelte"; import Issues from "@app/views/repo/Issues.svelte"; import Patch from "@app/views/repo/Patch.svelte"; @@ -36,11 +44,45 @@ import Command from "./components/Command.svelte"; import ExternalLink from "./components/ExternalLink.svelte"; + import FullscreenModalPortal from "./components/FullscreenModalPortal.svelte"; import FullWindowError from "./components/FullWindowError.svelte"; import Spinner from "./components/Spinner.svelte"; const activeRouteStore = router.activeRouteStore; + const DRAG_REGION_HEIGHT = 32; + const INTERACTIVE_TAGS = new Set([ + "a", + "button", + "input", + "select", + "textarea", + ]); + + function isDraggableArea(e: MouseEvent): boolean { + if (e.clientY > DRAG_REGION_HEIGHT) return false; + let el = e.target as HTMLElement | null; + while (el && el !== document.body) { + if (INTERACTIVE_TAGS.has(el.tagName.toLowerCase())) return false; + if (el.getAttribute("role") === "button") return false; + if (el.classList.contains("txt-selectable")) return false; + el = el.parentElement; + } + return true; + } + + window + .matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", ({ matches }) => { + if (get(followSystemTheme)) { + theme.set(matches ? "dark" : "light"); + } + }); + + if (get(followSystemTheme)) { + theme.set(loadTheme()); + } + let profile = $state(); let showSpinner = $state(false); @@ -90,6 +132,9 @@ ), ); $effect(() => document.documentElement.setAttribute("data-theme", $theme)); + $effect(() => + document.documentElement.setAttribute("data-codefont", $codeFont), + ); { + if (window.__TAURI_INTERNALS__ && isDraggableArea(e)) { + void getCurrentWindow().startDragging(); + } + }} onkeydown={e => { const auxiliarKey = isMac() ? e.metaKey : e.ctrlKey; // Handles the position of the plus key on different keyboard layouts. const plusKey = e.key === "1" || e.key === "="; - if (auxiliarKey && (e.key === "+" || plusKey)) { + if (e.key === "Escape") { + hide(); + } else if (auxiliarKey && (e.key === "+" || plusKey)) { increaseFontSize(); } else if (auxiliarKey && e.key === "-") { decreaseFontSize(); @@ -114,6 +166,7 @@ resetFontSize(); } }} /> + {#if $activeRouteStore.resource === "booting"} {#if startup.error?.code === "IdentityError.MissingProfile"} @@ -141,12 +194,12 @@ {:else if showSpinner}
{/if} -{:else if $activeRouteStore.resource === "home"} - +{:else if $activeRouteStore.resource === "inbox"} + +{:else if $activeRouteStore.resource === "guide"} + {:else if $activeRouteStore.resource === "repo.home"} -{:else if $activeRouteStore.resource === "repo.createIssue"} - {:else if $activeRouteStore.resource === "repo.issue"} {:else if $activeRouteStore.resource === "repo.issues"} diff --git a/src/components/AddRepoButton.svelte b/src/components/AddRepoButton.svelte index d7262d1b2..33714e2ca 100644 --- a/src/components/AddRepoButton.svelte +++ b/src/components/AddRepoButton.svelte @@ -3,7 +3,7 @@ + + + + diff --git a/src/components/AssigneeInput.svelte b/src/components/AssigneeInput.svelte index 75eb74618..ca00763e7 100644 --- a/src/components/AssigneeInput.svelte +++ b/src/components/AssigneeInput.svelte @@ -8,6 +8,7 @@ publicKeyFromDid, } from "@app/lib/utils"; + import Button from "@app/components/Button.svelte"; import Icon from "@app/components/Icon.svelte"; import NodeId from "@app/components/NodeId.svelte"; import TextInput from "@app/components/TextInput.svelte"; @@ -17,6 +18,7 @@ assignees: Author[]; submitInProgress: boolean; save: (updatedAssignees: Author[]) => void; + preview?: boolean; } const { @@ -24,6 +26,7 @@ assignees = $bindable(), submitInProgress = false, save, + preview = false, }: Props = $props(); let updatedAssignees: Author[] = $state([]); @@ -94,33 +97,25 @@ -
- - - {#if allowedToEdit} -
- {#if showInput} - - { - inputValue = ""; - showInput = false; - }} - name="cross" /> +{#if preview} +
+
- {/if} -
- -{#if showInput} -
- - {#if !valid && validationMessage} -
- {validationMessage} -
- {/if} + + {#each updatedAssignees as assignee} + + + + {/each}
-{/if} +{:else} +
+ {#if showInput} +
+
+ +
+ +
+ {:else} + + {/if} -{#if updatedAssignees.length > 0} -
{#if allowedToEdit} {#each updatedAssignees as assignee} {/each} @@ -217,4 +205,10 @@ {/each} {/if}
+ + {#if !valid && validationMessage} +
+ {validationMessage} +
+ {/if} {/if} diff --git a/src/components/Avatar.svelte b/src/components/Avatar.svelte deleted file mode 100644 index c732ede59..000000000 --- a/src/components/Avatar.svelte +++ /dev/null @@ -1,34 +0,0 @@ - - - - -avatar diff --git a/src/components/BadgeCounterSwitch.svelte b/src/components/BadgeCounterSwitch.svelte new file mode 100644 index 000000000..11f55e129 --- /dev/null +++ b/src/components/BadgeCounterSwitch.svelte @@ -0,0 +1,45 @@ + + + + + + +
+ + +
diff --git a/src/components/Border.svelte b/src/components/Border.svelte deleted file mode 100644 index 170744eb9..000000000 --- a/src/components/Border.svelte +++ /dev/null @@ -1,293 +0,0 @@ - - - - - -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
- {@render children()} -
-
-
- -
-
-
-
-
- -
-
-
-
-
-
diff --git a/src/components/Button.svelte b/src/components/Button.svelte index a10878563..5ce162754 100644 --- a/src/components/Button.svelte +++ b/src/components/Button.svelte @@ -1,23 +1,29 @@
-
-
-
-
-
- -
-
-
-
-
- -
-
-
- {@render children()} -
-
-
- -
-
-
-
-
- -
-
-
-
-
+ {title}> + {@render children()}
diff --git a/src/components/Changes.svelte b/src/components/Changes.svelte index 67e90a713..1555530db 100644 --- a/src/components/Changes.svelte +++ b/src/components/Changes.svelte @@ -5,13 +5,13 @@ import { cachedGetDiff, cachedListCommits } from "@app/lib/invoke"; import { pluralize } from "@app/lib/utils"; + import Button from "@app/components/Button.svelte"; import Changeset from "@app/components/Changeset.svelte"; import CobCommitTeaser from "@app/components/CobCommitTeaser.svelte"; import CommitsContainer from "@app/components/CommitsContainer.svelte"; import Icon from "@app/components/Icon.svelte"; import Id from "@app/components/Id.svelte"; import JobCob from "@app/components/JobCob.svelte"; - import NakedButton from "@app/components/NakedButton.svelte"; interface Props { patchId: string; @@ -66,11 +66,11 @@ position: relative; display: flex; flex-direction: column; - font-size: 0.875rem; + font: var(--txt-body-m-regular); margin-left: 0.5rem; gap: 0.5rem; padding: 1rem 0.5rem 0.5rem 1rem; - border-left: 1px solid var(--color-fill-separator); + border-left: 1px solid var(--color-border-subtle); } .commit:last-of-type::after { content: ""; @@ -78,7 +78,7 @@ left: -18.5px; top: 14px; bottom: -0.5rem; - border-left: 4px solid var(--color-background-default); + border-left: 4px solid var(--color-surface-canvas); } .commit-dot { width: 0.25rem; @@ -86,30 +86,30 @@ position: absolute; top: 0.625rem; left: -18.5px; - background-color: var(--color-fill-separator); + background-color: var(--color-border-subtle); } .commit { cursor: pointer; } .commit-dot.active { - background-color: var(--color-border-focus); + background-color: var(--color-border-brand); } .commit:hover:not(.single-commit) .commit-dot:not(.active) { - background-color: var(--color-foreground-contrast); + background-color: var(--color-text-primary); } .commit:hover:not(.single-commit) { - background-color: var(--color-background-float); + background-color: var(--color-surface-canvas); } .disabled { - color: var(--color-foreground-disabled) !important; + color: var(--color-text-disabled) !important; } .summary { cursor: pointer; padding: 0.25rem 0; } .summary:hover:not(.single-commit) { - background-color: var(--color-background-float); - color: var(--color-foreground-contrast) !important; + background-color: var(--color-surface-canvas); + color: var(--color-text-primary) !important; } .single-commit { cursor: default !important; @@ -117,30 +117,25 @@
- (hideChanges = !hideChanges)} - stylePadding="0 4px"> + +
Changes
{#if !hideChanges}
- (filesExpanded = !filesExpanded)}> +
{/if}
@@ -150,13 +145,13 @@
{#snippet leftHeader()} -
Commits
+
Commits
{/snippet}
{ @@ -169,11 +164,8 @@ {commits.length} {pluralize("commit", commits.length)} on base - -
Base
+ +
Base
{#each [...commits].reverse() as commit, idx} @@ -211,7 +203,7 @@ {/await} {#await cachedGetDiff(rid, { base, head, unified: 3, highlight: true })} - Loading… + Loading… {:then diff} {/await} diff --git a/src/components/CheckoutPatchButton.svelte b/src/components/CheckoutPatchButton.svelte index 80c985812..927704198 100644 --- a/src/components/CheckoutPatchButton.svelte +++ b/src/components/CheckoutPatchButton.svelte @@ -1,10 +1,9 @@ - + {#snippet toggle(onclick)} - Checkout patch - + {/snippet} {#snippet popover()} - - +
+ To checkout this patch in your working copy, run: - +
{/snippet}
diff --git a/src/components/CheckoutRepoButton.svelte b/src/components/CheckoutRepoButton.svelte index dc3522389..e5089dbd8 100644 --- a/src/components/CheckoutRepoButton.svelte +++ b/src/components/CheckoutRepoButton.svelte @@ -1,5 +1,4 @@ - + {#snippet toggle(onclick)} +
+
+
diff --git a/src/components/CobCommitTeaser.svelte b/src/components/CobCommitTeaser.svelte index 01ed6c2fb..0c76e684e 100644 --- a/src/components/CobCommitTeaser.svelte +++ b/src/components/CobCommitTeaser.svelte @@ -4,11 +4,11 @@ import { twemoji } from "@app/lib/utils"; + import Button from "@app/components/Button.svelte"; import CompactCommitAuthorship from "@app/components/CompactCommitAuthorship.svelte"; import Icon from "@app/components/Icon.svelte"; import Id from "@app/components/Id.svelte"; import InlineTitle from "@app/components/InlineTitle.svelte"; - import NakedButton from "@app/components/NakedButton.svelte"; interface Props { children?: Snippet; @@ -32,7 +32,7 @@ + +
+ + + +
diff --git a/src/components/Command.svelte b/src/components/Command.svelte index 67f4aa358..3004acbc8 100644 --- a/src/components/Command.svelte +++ b/src/components/Command.svelte @@ -1,5 +1,4 @@ {#if closed} - (closed = false)}> - - + + {:else} -
+
- (closed = true)}> - Cancel - +
{/if} diff --git a/src/components/CopyableId.svelte b/src/components/CopyableId.svelte index a2633df08..04dbc64f8 100644 --- a/src/components/CopyableId.svelte +++ b/src/components/CopyableId.svelte @@ -7,7 +7,13 @@ inline = false, children, id, - }: { inline?: boolean; children?: Snippet; id: string } = $props(); + styleFont = undefined, + }: { + inline?: boolean; + children?: Snippet; + id: string; + styleFont?: string; + } = $props(); let clipboard: Clipboard; @@ -15,12 +21,12 @@ -
+
+{stats.insertions}
-{stats.deletions}
diff --git a/src/components/Discussion.svelte b/src/components/Discussion.svelte index 8c2f8ef42..ac0089e00 100644 --- a/src/components/Discussion.svelte +++ b/src/components/Discussion.svelte @@ -11,9 +11,9 @@ import * as roles from "@app/lib/roles"; import { scrollIntoView } from "@app/lib/utils"; + import Button from "@app/components/Button.svelte"; import CommentToggleInput from "@app/components/CommentToggleInput.svelte"; import Icon from "@app/components/Icon.svelte"; - import NakedButton from "@app/components/NakedButton.svelte"; import ThreadComponent from "@app/components/Thread.svelte"; interface Props { @@ -84,29 +84,28 @@
- (hideDiscussion = !hideDiscussion)}> - +
- Discussion + Discussion {sum( commentThreads.map(t => { return t.replies.length + 1; @@ -116,8 +115,8 @@
- { if (hideDiscussion) { @@ -130,8 +129,8 @@ await toggleReply(); }}> - Comment - + Comment +
diff --git a/src/components/DropdownListItem.svelte b/src/components/DropdownListItem.svelte index 41c51f325..32f6613f0 100644 --- a/src/components/DropdownListItem.svelte +++ b/src/components/DropdownListItem.svelte @@ -33,32 +33,30 @@ min-height: 2rem; padding: 0 0.75rem; white-space: nowrap; - font-size: var(--font-size-small); - font-weight: var(--font-weight-regular); - color: var(--color-foreground-contrast); - clip-path: var(--1px-corner-fill); + font: var(--txt-body-m-regular); + color: var(--color-text-primary); + border-radius: var(--border-radius-sm); } .item.disabled { - color: var(--color-foreground-disabled); + color: var(--color-text-disabled); } .item:hover, .selected { - background-color: var(--color-fill-ghost); + background-color: var(--color-surface-subtle); } .selected { - font-weight: var(--font-weight-semibold); - color: var(--color-foreground-contrast); - background-color: var(--color-fill-ghost); + color: var(--color-text-primary); + background-color: var(--color-surface-subtle); } .item:hover.selected { - background-color: var(--color-fill-ghost-hover); + background-color: var(--color-surface-mid); } .item:hover.selected.disabled { - background-color: var(--color-fill-ghost); + background-color: var(--color-surface-subtle); } .item:hover.disabled { cursor: not-allowed; - background-color: var(--color-background-float); + background-color: var(--color-surface-canvas); } diff --git a/src/components/EditableTitle.svelte b/src/components/EditableTitle.svelte index 552c55456..63978afa2 100644 --- a/src/components/EditableTitle.svelte +++ b/src/components/EditableTitle.svelte @@ -1,7 +1,7 @@ - + {#if children} {@render children()} + {:else} - {href} + {/if} - diff --git a/src/components/File.svelte b/src/components/FileBlock.svelte similarity index 63% rename from src/components/File.svelte rename to src/components/FileBlock.svelte index c45c9bf78..72f91e076 100644 --- a/src/components/File.svelte +++ b/src/components/FileBlock.svelte @@ -3,8 +3,8 @@ import { tick } from "svelte"; + import Button from "@app/components/Button.svelte"; import Icon from "@app/components/Icon.svelte"; - import NakedButton from "@app/components/NakedButton.svelte"; interface Props { children: Snippet; @@ -13,6 +13,7 @@ leftHeader?: Snippet; rightHeader?: Snippet; sticky?: boolean; + border?: boolean; } /* eslint-disable prefer-const */ @@ -23,6 +24,7 @@ rightHeader, sticky = true, expandable = true, + border = true, }: Props = $props(); /* eslint-enable prefer-const */ @@ -34,25 +36,13 @@ display: flex; align-items: center; height: 2.5rem; - padding-left: 0.5rem; + padding-left: 0.25rem; z-index: 2; - font-size: var(--font-size-small); - background-color: var(--color-background-default); + font: var(--txt-body-m-regular); position: relative; - } - .header::after { - position: absolute; - z-index: -1; - content: " "; - background-color: var(--color-fill-float-hover); - clip-path: var(--2px-top-corner-fill); - width: 100%; - height: 100%; - top: 0; - left: 0; - } - .header.collapsed { - clip-path: var(--2px-corner-fill); + background-color: var(--color-surface-canvas); + border-top-left-radius: var(--border-radius-md); + border-top-right-radius: var(--border-radius-md); } .sticky { @@ -71,26 +61,25 @@ position: relative; overflow-x: auto; z-index: 1; - } - .container::after { - position: absolute; - z-index: -1; - content: " "; - clip-path: var(--2px-bottom-corner-fill); - background-color: var(--color-background-float); - width: 100%; - height: 100%; - top: 0; - left: 0; + border-top: none; + border-bottom-left-radius: var(--border-radius-md); + border-bottom-right-radius: var(--border-radius-md); } -
+
{#if expandable} - { expanded = !expanded; if (!expanded && header) { @@ -99,7 +88,7 @@ } }}> - + {/if} {@render leftHeader?.()}
@@ -115,7 +104,10 @@
{#if expanded} -
+
{@render children()}
{/if} diff --git a/src/components/FileDiff.svelte b/src/components/FileDiff.svelte index 5c87e31fe..78b3c400a 100644 --- a/src/components/FileDiff.svelte +++ b/src/components/FileDiff.svelte @@ -3,7 +3,7 @@ import type { FileDiff } from "@bindings/diff/FileDiff"; import Diff from "@app/components/Diff.svelte"; - import File from "@app/components/File.svelte"; + import FileBlock from "@app/components/FileBlock.svelte"; import Icon from "@app/components/Icon.svelte"; import Path from "@app/components/Path.svelte"; @@ -58,32 +58,30 @@ {#snippet emptyPlaceholder()}
- Empty file + Empty file
{/snippet} - + {#snippet leftHeader()} {#if file.status === "moved" || file.status === "copied"} @@ -96,23 +94,23 @@ {/if} {#if file.status === "added"} - Added + Added {:else if file.status === "deleted"} - Deleted + Deleted {:else if file.status === "moved"} - Moved + Moved {:else if file.status === "copied"} - Copied + Copied {/if} {/snippet} {#snippet rightHeader()} {#if file.diff.type === "plain" && file.diff.hunks.length > 0}
- + +{file.diff.stats.additions} - + -{file.diff.stats.deletions}
@@ -149,4 +147,4 @@ {:else} {@render emptyPlaceholder()} {/if} -
+ diff --git a/src/components/Source/File.svelte b/src/components/FileTreeFile.svelte similarity index 55% rename from src/components/Source/File.svelte rename to src/components/FileTreeFile.svelte index f6daacf3c..509322ff0 100644 --- a/src/components/Source/File.svelte +++ b/src/components/FileTreeFile.svelte @@ -5,25 +5,32 @@ name, fetchBlob, active, - }: { name: string; fetchBlob: () => Promise; active: boolean } = - $props(); + indent = 0.5, + }: { + name: string; + fetchBlob: () => Promise; + active: boolean; + indent?: number; + } = $props(); @@ -32,14 +39,14 @@
- +
-
+
{name}
diff --git a/src/components/FileTreeFolder.svelte b/src/components/FileTreeFolder.svelte new file mode 100644 index 000000000..d30370d9f --- /dev/null +++ b/src/components/FileTreeFolder.svelte @@ -0,0 +1,84 @@ + + + + + + +
(expanded = !expanded)}> +
+
+ +
+ {name} +
+
+{#if expanded} + {#await treePromise then tree} + {#if tree} +
+ {#each tree.entries as entry (entry.path)} + {#if entry.kind === "tree"} + + {:else if entry.kind === "blob"} + fetchBlob(entry.path)} + active={entry.path === currentPath} + indent={indent + 1.5} /> + {/if} + {/each} +
+ {/if} + {/await} +{/if} diff --git a/src/components/FontSizeSwitch.svelte b/src/components/FontSizeSwitch.svelte index db8e0e6c9..52e144d40 100644 --- a/src/components/FontSizeSwitch.svelte +++ b/src/components/FontSizeSwitch.svelte @@ -14,21 +14,21 @@
- + {/if} +{/if} diff --git a/src/components/GuideButton.svelte b/src/components/GuideButton.svelte deleted file mode 100644 index 50e68985a..000000000 --- a/src/components/GuideButton.svelte +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - - - {#snippet toggle(onclick)} - - Guide - - {/snippet} - {#snippet popover()} - -
-
- Get started -
-
- Hello - , - - your identity has been created and stored on your machine. -
-
- Your public key is - {truncateDid(config.publicKey)} - - you can share this with anyone to find you on the network. -
-
- We release a new version of the app every two weeks. To stay up to - date, go to - - and enable 'Check for updates' to receive notifications about new releases. -
-
- {#if radicleInstalled() || $nodeRunning} -
-
- Radicle CLI is installed, you're good to - go. -
- -
- {:else} -
-
- Radicle CLI is not installed -
-
- To interact with repositories on the Radicle network, you’ll need - to install Radicle node along with its accompanying CLI tools. The - node runs in the background, enabling seamless pushing and pulling - of changes, while the CLI tools let you manage the node and - provide interoperability between Git and Radicle. -
-
- To install Radicle node and CLI tooling, run this in your shell: -
- -
- {/if} -
-
- {/snippet} -
diff --git a/src/components/Header.svelte b/src/components/Header.svelte deleted file mode 100644 index 973268fef..000000000 --- a/src/components/Header.svelte +++ /dev/null @@ -1,120 +0,0 @@ - - - - -
-
-
-
- { - window.history.back(); - }} - stylePadding="0 4px"> - - - { - window.history.forward(); - }} - stylePadding="0 4px"> - - -
- {@render breadcrumbs?.()} -
-
- -
- - - -
-
-
-
diff --git a/src/components/HomeSidebar.svelte b/src/components/HomeSidebar.svelte deleted file mode 100644 index f20f8b11d..000000000 --- a/src/components/HomeSidebar.svelte +++ /dev/null @@ -1,117 +0,0 @@ - - - - - diff --git a/src/components/HoverPopover.svelte b/src/components/HoverPopover.svelte index 2d2f432e3..d9d2e5498 100644 --- a/src/components/HoverPopover.svelte +++ b/src/components/HoverPopover.svelte @@ -1,31 +1,63 @@ -
+ +
{ + hide.cancel(); + show(); + }} + onmouseleave={() => { + show.cancel(); + hide(); + }}>
{ e.stopPropagation(); - }} - onmouseenter={() => setVisible(true)} - onmouseleave={() => setVisible(false)}> + }}> {@render toggle()} - - {#if visible} -
-
- {@render popover()} -
-
- {/if}
+ + {#if visible} + +
{ + hide.cancel(); + show(); + }} + onmouseleave={() => { + show.cancel(); + hide(); + }}> + {@render popover()} +
+ {/if}
diff --git a/src/components/Icon.svelte b/src/components/Icon.svelte index 6d36b0576..21c0bd737 100644 --- a/src/components/Icon.svelte +++ b/src/components/Icon.svelte @@ -1,100 +1,128 @@ -
+
{ @@ -81,7 +118,7 @@ onmouseleave={() => { setVisible(false); }} - class="target-{variant} global-{variant}" + class="txt-id" style:cursor="pointer" aria-label={ariaLabel} onclick={async event => { @@ -99,11 +136,9 @@
{#if visible} -
-
- - {tooltip} -
+
+ + {tooltip}
{/if}
diff --git a/src/components/IdentityButton.svelte b/src/components/IdentityButton.svelte new file mode 100644 index 000000000..8a8394fae --- /dev/null +++ b/src/components/IdentityButton.svelte @@ -0,0 +1,73 @@ + + + + {#snippet toggle(onclick)} + + {/snippet} + {#snippet popover()} +
+ { + await writeToClipboard(didFromPublicKey(config.publicKey)); + copyIcon = "checkmark"; + restoreCopyIcon(); + closeFocused(); + }}> + + {truncateDid(config.publicKey)} + + + + + + view on seed.radicle.garden + + + +
+ {/snippet} +
diff --git a/src/components/InboxButton.svelte b/src/components/InboxButton.svelte deleted file mode 100644 index ff1d3f686..000000000 --- a/src/components/InboxButton.svelte +++ /dev/null @@ -1,162 +0,0 @@ - - - - {#snippet toggle(onclick)} - { - buttonActive = true; - await loadNotifications(); - onclick(); - }} - variant={notificationCount && notificationCount > 0 - ? "secondary" - : "ghost"} - active={buttonActive}> - - {#if notificationCount !== undefined && notificationCount > 0} - {notificationCount} - {/if} - - {/snippet} - - {#snippet popover()} - - - - {/snippet} - diff --git a/src/components/Inbox.svelte b/src/components/InboxList.svelte similarity index 79% rename from src/components/Inbox.svelte rename to src/components/InboxList.svelte index 88e4cde24..6eb553e9e 100644 --- a/src/components/Inbox.svelte +++ b/src/components/InboxList.svelte @@ -1,9 +1,8 @@ - - - -
-
- - { - if (showFilters) { - showFilters = false; - searchInput = ""; - } else { - showFilters = true; - } - }}> - - -
-
- { - if ($activeRouteStore.resource === "repo.createIssue") { - window.history.back(); - } else { - void router.push({ - resource: "repo.createIssue", - rid: repo.rid, - status, - }); - } - }}> - New issue - -
-
- -{#if showFilters} -
- { - if (searchResults.length === 1) { - await router.push({ - resource: "repo.issue", - rid: repo.rid, - issue: searchResults[0].obj.issue.id, - status, - }); - } - }} - onDismiss={() => { - showFilters = false; - searchInput = ""; - }} - placeholder={`Fuzzy filter issues ${modifierKey()} + f`} - autofocus - keyShortcuts="ctrl+f" - bind:value={searchInput}> - {#snippet left()} -
- -
- {/snippet} -
-
-{/if} - -{#if searchResults.length > 0} -
- {#each searchResults as result} - - {/each} -
-{/if} - -{#if searchResults.length === 0} - -
-
- - {#if issues.length > 0 && searchResults.length === 0} - No matching issues. - {:else} - No {status === "all" ? "" : status} issues. - {/if} -
-
-
-{/if} diff --git a/src/components/IssueStateButton.svelte b/src/components/IssueStateButton.svelte index 07a163a72..e7d558e18 100644 --- a/src/components/IssueStateButton.svelte +++ b/src/components/IssueStateButton.svelte @@ -6,7 +6,7 @@ import { issueStatusBackgroundColor, issueStatusColor } from "@app/lib/utils"; - import Border from "@app/components/Border.svelte"; + import Button from "@app/components/Button.svelte"; import DropdownList from "@app/components/DropdownList.svelte"; import DropdownListItem from "@app/components/DropdownListItem.svelte"; import Icon from "@app/components/Icon.svelte"; @@ -16,56 +16,57 @@ interface Props { selectedState: State; onSelect: (selectedStatus: State) => void; + disabled?: boolean; } - const { selectedState, onSelect }: Props = $props(); + const { selectedState, onSelect, disabled = false }: Props = $props(); let popoverExpanded: boolean = $state(false); - - {#snippet toggle(onclick)} - + + {/snippet} {#snippet popover()} - +
{ onSelect(state); closeFocused(); }}> + class="global-chip" + style:padding="0" + style:margin-left="-0.5rem" + style:color={issueStatusColor[state.status]} + style:background-color={issueStatusBackgroundColor[state.status]}> + + {capitalize(state.status)} {state.status === "closed" ? `as ${state.reason}` : ""} {/snippet} - +
{/snippet}
diff --git a/src/components/IssueStateFilterButton.svelte b/src/components/IssueStateFilterButton.svelte deleted file mode 100644 index 94975ec67..000000000 --- a/src/components/IssueStateFilterButton.svelte +++ /dev/null @@ -1,83 +0,0 @@ - - -{#snippet iconSnippet(status: IssueStatus)} -
- -
-{/snippet} - -{#snippet counterSnippet(status: IssueStatus)} -
- {#if status === "all"} - {counters.open + counters.closed} - {:else} - {counters[status]} - {/if} -
-{/snippet} - - - {#snippet toggle(onclick)} - - {@render iconSnippet(status)} - {capitalize(status)} - {@render counterSnippet(status)} - - - {/snippet} - - {#snippet popover()} - - - {#snippet item(state)} - { - changeFilter(state); - closeFocused(); - }}> - {@render iconSnippet(state)} - {capitalize(state)} - {@render counterSnippet(state)} - - {/snippet} - - - {/snippet} - diff --git a/src/components/IssueTeaser.svelte b/src/components/IssueTeaser.svelte index 584e38c5a..fb1fc1b46 100644 --- a/src/components/IssueTeaser.svelte +++ b/src/components/IssueTeaser.svelte @@ -10,7 +10,6 @@ issueStatusColor, } from "@app/lib/utils"; - import Border from "@app/components/Border.svelte"; import Icon from "@app/components/Icon.svelte"; import Id from "@app/components/Id.svelte"; import InlineTitle from "@app/components/InlineTitle.svelte"; @@ -18,22 +17,13 @@ import NodeId from "@app/components/NodeId.svelte"; interface Props { - compact?: boolean; focussed?: boolean; issue: Issue; rid: string; - selected?: boolean; status: IssueStatus; } - const { - compact = false, - focussed, - issue, - rid, - selected = false, - status, - }: Props = $props(); + const { focussed, issue, rid, status }: Props = $props(); @@ -76,7 +63,6 @@ tabindex="0" role="button" class="issue-teaser" - class:selected style:align-items="flex-start" style:clip-path={focussed ? "none" : undefined} style:padding={focussed ? "1rem" : "20px"} @@ -85,7 +71,7 @@ }}>
{#if issue.state.status === "open"} @@ -99,24 +85,29 @@ style:flex-direction="column" style:align-items="flex-start"> -
+
opened - + {formatTimestamp(issue.timestamp)}
- {#if !compact} - {#each issue.labels as label} -