mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-09-11 12:25:42 +03:00
feat: create separate component library subpackage
This commit is contained in:
parent
6da1b444ba
commit
091b45d5e9
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "@gitbutler/ui",
|
||||
"name": "@gitbutler/app",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
@ -121,10 +121,5 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"openai": "^4.47.3"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"plugin:storybook/recommended"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"build": {
|
||||
"beforeBuildCommand": "pnpm build && cargo build --release -p gitbutler-git && bash ./gitbutler-tauri/inject-git-binaries.sh"
|
||||
"beforeBuildCommand": "pnpm build:app && cargo build --release -p gitbutler-git && bash ./gitbutler-tauri/inject-git-binaries.sh"
|
||||
},
|
||||
"package": {
|
||||
"productName": "GitButler"
|
||||
|
23
package.json
23
package.json
@ -1,22 +1,23 @@
|
||||
{
|
||||
"name": "git-butler-tauri",
|
||||
"name": "git-butler",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
},
|
||||
"packageManager": "pnpm@9.2.0",
|
||||
"scripts": {
|
||||
"dev": "pnpm --filter @gitbutler/ui run dev",
|
||||
"test": "pnpm --filter @gitbutler/ui run test",
|
||||
"test:watch": "pnpm --filter @gitbutler/ui run test:watch",
|
||||
"build": "pnpm --filter @gitbutler/ui run build",
|
||||
"build:nightly": "pnpm --filter @gitbutler/ui run build:nightly",
|
||||
"build:development": "pnpm --filter @gitbutler/ui run build:development",
|
||||
"check": "pnpm --filter @gitbutler/ui run check",
|
||||
"lint": "pnpm --filter @gitbutler/ui run lint",
|
||||
"format": "pnpm --filter @gitbutler/ui run format",
|
||||
"dev": "pnpm --filter @gitbutler/app run dev",
|
||||
"test": "pnpm --filter @gitbutler/app run test",
|
||||
"test:watch": "pnpm --filter @gitbutler/app run test:watch",
|
||||
"build:ui": "pnpm --filter @gitbutler/ui run build",
|
||||
"build:app": "pnpm --filter @gitbutler/app run build",
|
||||
"build:nightly": "pnpm --filter @gitbutler/app run build:nightly",
|
||||
"build:development": "pnpm --filter @gitbutler/app run build:development",
|
||||
"check": "pnpm --filter @gitbutler/app run check",
|
||||
"lint": "pnpm --filter @gitbutler/app run lint",
|
||||
"format": "pnpm --filter @gitbutler/app run format",
|
||||
"tauri": "tauri",
|
||||
"prepare": "pnpm --filter @gitbutler/ui run prepare",
|
||||
"prepare": "pnpm --filter @gitbutler/app run prepare",
|
||||
"rustfmt": "cargo +nightly fmt -- --config-path rustfmt-nightly.toml"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
183
pnpm-lock.yaml
183
pnpm-lock.yaml
@ -292,6 +292,132 @@ importers:
|
||||
specifier: ^0.34.6
|
||||
version: 0.34.6(playwright@1.44.1)
|
||||
|
||||
ui:
|
||||
devDependencies:
|
||||
'@chromatic-com/storybook':
|
||||
specifier: ^1.5.0
|
||||
version: 1.5.0(react@18.3.1)
|
||||
'@eslint/js':
|
||||
specifier: ^9.5.0
|
||||
version: 9.5.0
|
||||
'@storybook/addon-essentials':
|
||||
specifier: ^8.1.10
|
||||
version: 8.1.10(@types/react@18.3.3)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@storybook/addon-interactions':
|
||||
specifier: ^8.1.10
|
||||
version: 8.1.10(vitest@0.34.6(playwright@1.44.1))
|
||||
'@storybook/addon-links':
|
||||
specifier: ^8.1.10
|
||||
version: 8.1.10(react@18.3.1)
|
||||
'@storybook/blocks':
|
||||
specifier: ^8.1.10
|
||||
version: 8.1.10(@types/react@18.3.3)(prettier@3.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@storybook/svelte':
|
||||
specifier: ^8.1.10
|
||||
version: 8.1.10(prettier@3.3.2)(svelte@5.0.0-next.149)
|
||||
'@storybook/sveltekit':
|
||||
specifier: ^8.1.10
|
||||
version: 8.1.10(@babel/core@7.24.7)(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.149)(vite@5.2.13(@types/node@20.5.9)))(postcss-load-config@5.1.0(postcss@8.4.38))(postcss@8.4.38)(prettier@3.3.2)(svelte@5.0.0-next.149)(typescript@5.4.5)(vite@5.2.13(@types/node@20.5.9))
|
||||
'@storybook/test':
|
||||
specifier: ^8.1.10
|
||||
version: 8.1.10(vitest@0.34.6(playwright@1.44.1))
|
||||
'@sveltejs/adapter-static':
|
||||
specifier: ^3.0.1
|
||||
version: 3.0.1(@sveltejs/kit@2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.149)(vite@5.2.13(@types/node@20.5.9)))(svelte@5.0.0-next.149)(vite@5.2.13(@types/node@20.5.9)))
|
||||
'@sveltejs/kit':
|
||||
specifier: ^2.5.10
|
||||
version: 2.5.10(@sveltejs/vite-plugin-svelte@3.1.1(svelte@5.0.0-next.149)(vite@5.2.13(@types/node@20.5.9)))(svelte@5.0.0-next.149)(vite@5.2.13(@types/node@20.5.9))
|
||||
'@sveltejs/vite-plugin-svelte':
|
||||
specifier: ^3.1.1
|
||||
version: 3.1.1(svelte@5.0.0-next.149)(vite@5.2.13(@types/node@20.5.9))
|
||||
'@types/crypto-js':
|
||||
specifier: ^4.2.2
|
||||
version: 4.2.2
|
||||
'@types/diff':
|
||||
specifier: ^5.2.1
|
||||
version: 5.2.1
|
||||
'@types/diff-match-patch':
|
||||
specifier: ^1.0.36
|
||||
version: 1.0.36
|
||||
'@types/eslint__js':
|
||||
specifier: ^8.42.3
|
||||
version: 8.42.3
|
||||
'@types/git-url-parse':
|
||||
specifier: ^9.0.3
|
||||
version: 9.0.3
|
||||
'@types/lscache':
|
||||
specifier: ^1.3.4
|
||||
version: 1.3.4
|
||||
'@types/marked':
|
||||
specifier: ^5.0.2
|
||||
version: 5.0.2
|
||||
'@typescript-eslint/parser':
|
||||
specifier: ^7.13.1
|
||||
version: 7.13.1(eslint@9.5.0)(typescript@5.4.5)
|
||||
eslint:
|
||||
specifier: ^9.5.0
|
||||
version: 9.5.0
|
||||
eslint-config-prettier:
|
||||
specifier: ^9.1.0
|
||||
version: 9.1.0(eslint@9.5.0)
|
||||
eslint-import-resolver-typescript:
|
||||
specifier: ^3.6.1
|
||||
version: 3.6.1(@typescript-eslint/parser@7.13.1(eslint@9.5.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1)(eslint@9.5.0)
|
||||
eslint-plugin-import-x:
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1(eslint@9.5.0)(typescript@5.4.5)
|
||||
eslint-plugin-storybook:
|
||||
specifier: ^0.8.0
|
||||
version: 0.8.0(eslint@9.5.0)(typescript@5.4.5)
|
||||
eslint-plugin-svelte:
|
||||
specifier: 2.40.0
|
||||
version: 2.40.0(eslint@9.5.0)(svelte@5.0.0-next.149)
|
||||
globals:
|
||||
specifier: ^15.6.0
|
||||
version: 15.6.0
|
||||
prettier:
|
||||
specifier: ^3.3.2
|
||||
version: 3.3.2
|
||||
prettier-plugin-svelte:
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(prettier@3.3.2)(svelte@5.0.0-next.149)
|
||||
storybook:
|
||||
specifier: ^8.1.10
|
||||
version: 8.1.10(@babel/preset-env@7.24.7(@babel/core@7.24.7))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
svelte:
|
||||
specifier: 5.0.0-next.149
|
||||
version: 5.0.0-next.149
|
||||
svelte-check:
|
||||
specifier: ^3.8.0
|
||||
version: 3.8.0(@babel/core@7.24.7)(postcss-load-config@5.1.0(postcss@8.4.38))(postcss@8.4.38)(svelte@5.0.0-next.149)
|
||||
svelte-eslint-parser:
|
||||
specifier: github:gitbutlerapp/svelte-eslint-parser
|
||||
version: https://codeload.github.com/gitbutlerapp/svelte-eslint-parser/tar.gz/839ccb1266e4b3660d0b5ca4daf735febb1e06f0(svelte@5.0.0-next.149)
|
||||
svelte-floating-ui:
|
||||
specifier: ^1.5.8
|
||||
version: 1.5.8
|
||||
svelte-french-toast:
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0(svelte@5.0.0-next.149)
|
||||
svelte-loadable-store:
|
||||
specifier: ^2.0.1
|
||||
version: 2.0.1(svelte@5.0.0-next.149)
|
||||
svelte-resize-observer:
|
||||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
typescript:
|
||||
specifier: ^5.4.5
|
||||
version: 5.4.5
|
||||
typescript-eslint:
|
||||
specifier: ^7.13.1
|
||||
version: 7.13.1(eslint@9.5.0)(typescript@5.4.5)
|
||||
vite:
|
||||
specifier: ^5.2.13
|
||||
version: 5.2.13(@types/node@20.5.9)
|
||||
vitest:
|
||||
specifier: ^0.34.6
|
||||
version: 0.34.6(playwright@1.44.1)
|
||||
|
||||
packages:
|
||||
|
||||
'@aashutoshrathi/word-wrap@1.2.6':
|
||||
@ -1233,10 +1359,6 @@ packages:
|
||||
resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==}
|
||||
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.3':
|
||||
resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.5':
|
||||
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@ -1245,10 +1367,6 @@ packages:
|
||||
resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/set-array@1.1.2':
|
||||
resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@jridgewell/set-array@1.2.1':
|
||||
resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
@ -2641,11 +2759,6 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
acorn@8.11.3:
|
||||
resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
acorn@8.12.0:
|
||||
resolution: {integrity: sha512-RTvkC4w+KNXrM39/lWCUaG0IbRkWdCv7W/IOW9oU6SawyxulvkQy5HQPVTKxEjczcUvapcrw3cFx/60VN/NRNw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@ -5973,8 +6086,8 @@ snapshots:
|
||||
|
||||
'@ampproject/remapping@2.2.1':
|
||||
dependencies:
|
||||
'@jridgewell/gen-mapping': 0.3.3
|
||||
'@jridgewell/trace-mapping': 0.3.19
|
||||
'@jridgewell/gen-mapping': 0.3.5
|
||||
'@jridgewell/trace-mapping': 0.3.25
|
||||
|
||||
'@aw-web-design/x-default-browser@1.4.126':
|
||||
dependencies:
|
||||
@ -7136,12 +7249,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@sinclair/typebox': 0.27.8
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.3':
|
||||
dependencies:
|
||||
'@jridgewell/set-array': 1.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
'@jridgewell/trace-mapping': 0.3.19
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.5':
|
||||
dependencies:
|
||||
'@jridgewell/set-array': 1.2.1
|
||||
@ -7150,8 +7257,6 @@ snapshots:
|
||||
|
||||
'@jridgewell/resolve-uri@3.1.1': {}
|
||||
|
||||
'@jridgewell/set-array@1.1.2': {}
|
||||
|
||||
'@jridgewell/set-array@1.2.1': {}
|
||||
|
||||
'@jridgewell/sourcemap-codec@1.4.15': {}
|
||||
@ -9147,29 +9252,27 @@ snapshots:
|
||||
mime-types: 2.1.35
|
||||
negotiator: 0.6.3
|
||||
|
||||
acorn-import-assertions@1.9.0(acorn@8.11.3):
|
||||
acorn-import-assertions@1.9.0(acorn@8.12.0):
|
||||
dependencies:
|
||||
acorn: 8.11.3
|
||||
acorn: 8.12.0
|
||||
optional: true
|
||||
|
||||
acorn-import-attributes@1.9.5(acorn@8.11.3):
|
||||
acorn-import-attributes@1.9.5(acorn@8.12.0):
|
||||
dependencies:
|
||||
acorn: 8.11.3
|
||||
acorn: 8.12.0
|
||||
|
||||
acorn-jsx@5.3.2(acorn@8.12.0):
|
||||
dependencies:
|
||||
acorn: 8.12.0
|
||||
|
||||
acorn-typescript@1.4.13(acorn@8.11.3):
|
||||
acorn-typescript@1.4.13(acorn@8.12.0):
|
||||
dependencies:
|
||||
acorn: 8.11.3
|
||||
acorn: 8.12.0
|
||||
|
||||
acorn-walk@8.2.0: {}
|
||||
|
||||
acorn@8.10.0: {}
|
||||
|
||||
acorn@8.11.3: {}
|
||||
|
||||
acorn@8.12.0: {}
|
||||
|
||||
address@1.2.2: {}
|
||||
@ -10737,23 +10840,23 @@ snapshots:
|
||||
|
||||
import-in-the-middle@1.4.2:
|
||||
dependencies:
|
||||
acorn: 8.11.3
|
||||
acorn-import-assertions: 1.9.0(acorn@8.11.3)
|
||||
acorn: 8.12.0
|
||||
acorn-import-assertions: 1.9.0(acorn@8.12.0)
|
||||
cjs-module-lexer: 1.3.1
|
||||
module-details-from-path: 1.0.3
|
||||
optional: true
|
||||
|
||||
import-in-the-middle@1.7.4:
|
||||
dependencies:
|
||||
acorn: 8.11.3
|
||||
acorn-import-attributes: 1.9.5(acorn@8.11.3)
|
||||
acorn: 8.12.0
|
||||
acorn-import-attributes: 1.9.5(acorn@8.12.0)
|
||||
cjs-module-lexer: 1.3.1
|
||||
module-details-from-path: 1.0.3
|
||||
|
||||
import-in-the-middle@1.8.0:
|
||||
dependencies:
|
||||
acorn: 8.11.3
|
||||
acorn-import-attributes: 1.9.5(acorn@8.11.3)
|
||||
acorn: 8.12.0
|
||||
acorn-import-attributes: 1.9.5(acorn@8.12.0)
|
||||
cjs-module-lexer: 1.3.1
|
||||
module-details-from-path: 1.0.3
|
||||
|
||||
@ -12253,15 +12356,15 @@ snapshots:
|
||||
'@ampproject/remapping': 2.2.1
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
'@types/estree': 1.0.5
|
||||
acorn: 8.11.3
|
||||
acorn-typescript: 1.4.13(acorn@8.11.3)
|
||||
acorn: 8.12.0
|
||||
acorn-typescript: 1.4.13(acorn@8.12.0)
|
||||
aria-query: 5.3.0
|
||||
axobject-query: 4.0.0
|
||||
esm-env: 1.0.0
|
||||
esrap: 1.2.2
|
||||
is-reference: 3.0.2
|
||||
locate-character: 3.0.0
|
||||
magic-string: 0.30.5
|
||||
magic-string: 0.30.10
|
||||
zimmerframe: 1.1.2
|
||||
|
||||
sveltedoc-parser@4.2.1:
|
||||
|
@ -1,2 +1,3 @@
|
||||
packages:
|
||||
- 'app'
|
||||
- 'ui'
|
||||
|
28
ui/.gitignore
vendored
Normal file
28
ui/.gitignore
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
build
|
||||
.svelte-kit
|
||||
package
|
||||
!.env.example
|
||||
vite.config.ts.timestamp-*
|
||||
|
||||
# Written to disk when using `act`
|
||||
.pnpm-store
|
||||
|
||||
# playwright
|
||||
test-results*
|
||||
playwright-report
|
||||
*storybook.log
|
22
ui/.prettierignore
Normal file
22
ui/.prettierignore
Normal file
@ -0,0 +1,22 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
butler/target
|
||||
/build
|
||||
/.svelte-kit
|
||||
/package
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Ignore files for PNPM, NPM and YARN
|
||||
pnpm-lock.yaml
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
|
||||
# Ignore not svelte dirs
|
||||
/.github
|
||||
/.vscode
|
||||
/src-tauri
|
||||
|
||||
# Written to disk when using `act`
|
||||
.pnpm-store
|
15
ui/.prettierrc.js
Normal file
15
ui/.prettierrc.js
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* @see https://prettier.io/docs/en/configuration.html
|
||||
* @type {import("prettier").Config}
|
||||
*/
|
||||
const config = {
|
||||
useTabs: true,
|
||||
singleQuote: true,
|
||||
trailingComma: 'none',
|
||||
printWidth: 100,
|
||||
plugins: ['prettier-plugin-svelte'],
|
||||
overrides: [{ files: '*.svelte', options: { parser: 'svelte' } }],
|
||||
endOfLine: 'auto'
|
||||
};
|
||||
|
||||
export default config;
|
@ -1,7 +1,7 @@
|
||||
import type { StorybookConfig } from '@storybook/sveltekit';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
||||
stories: ['../src/stories/**/*.mdx', '../src/stories/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
143
ui/eslint.config.js
Normal file
143
ui/eslint.config.js
Normal file
@ -0,0 +1,143 @@
|
||||
import js from '@eslint/js';
|
||||
import tsParser from '@typescript-eslint/parser';
|
||||
import eslintConfigPrettier from 'eslint-config-prettier';
|
||||
import eslintPluginSvelte from 'eslint-plugin-svelte';
|
||||
import globals from 'globals';
|
||||
import svelteParser from 'svelte-eslint-parser';
|
||||
import tsEslint from 'typescript-eslint';
|
||||
import pluginImportX from 'eslint-plugin-import-x';
|
||||
|
||||
export default tsEslint.config(
|
||||
js.configs.recommended,
|
||||
...tsEslint.configs.recommended,
|
||||
...eslintPluginSvelte.configs['flat/recommended'],
|
||||
eslintConfigPrettier,
|
||||
...eslintPluginSvelte.configs['flat/prettier'],
|
||||
{
|
||||
files: ['**/*.svelte'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2021,
|
||||
sourceType: 'module',
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.browser,
|
||||
$state: 'readonly',
|
||||
$derived: 'readonly',
|
||||
$props: 'readonly',
|
||||
$bindable: 'readonly',
|
||||
$inspect: 'readonly',
|
||||
$host: 'readonly'
|
||||
},
|
||||
parser: svelteParser,
|
||||
parserOptions: {
|
||||
parser: tsParser,
|
||||
extraFileExtensions: ['.svelte']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
'**/.*', // dotfiles aren't ignored by default in FlatConfig
|
||||
'.*', // dotfiles aren't ignored by default in FlatConfig
|
||||
'**/.DS_Store',
|
||||
'**/node_modules',
|
||||
'butler/target',
|
||||
'build',
|
||||
'.svelte-kit',
|
||||
'package',
|
||||
'e2e',
|
||||
'**/.env',
|
||||
'**/.env.*',
|
||||
'!**/.env.example',
|
||||
'**/pnpm-lock.yaml',
|
||||
'**/package-lock.json',
|
||||
'**/yarn.lock',
|
||||
'.github',
|
||||
'.vscode',
|
||||
'src-tauri',
|
||||
'eslint.config.js',
|
||||
'svelte.config.js',
|
||||
'postcss.config.cjs',
|
||||
'playwright.config.ts',
|
||||
'**/.pnpm-store'
|
||||
]
|
||||
},
|
||||
{
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
parser: tsEslint.parser,
|
||||
project: true,
|
||||
extraFileExtensions: ['.svelte']
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
eqeqeq: ['error', 'always'],
|
||||
'import-x/no-cycle': 'error',
|
||||
|
||||
'import-x/order': [
|
||||
'error',
|
||||
{
|
||||
alphabetize: {
|
||||
order: 'asc',
|
||||
orderImportKind: 'asc',
|
||||
caseInsensitive: false
|
||||
},
|
||||
|
||||
groups: [
|
||||
'index',
|
||||
'sibling',
|
||||
'parent',
|
||||
'internal',
|
||||
'external',
|
||||
'builtin',
|
||||
'object',
|
||||
'type'
|
||||
],
|
||||
|
||||
'newlines-between': 'never'
|
||||
}
|
||||
],
|
||||
|
||||
'import-x/no-unresolved': [
|
||||
'error',
|
||||
{
|
||||
ignore: ['^\\$app', '^\\$env']
|
||||
}
|
||||
],
|
||||
|
||||
'func-style': [2, 'declaration'],
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
argsIgnorePattern: '^_',
|
||||
varsIgnorePattern: '^_',
|
||||
caughtErrorsIgnorePattern: '^_'
|
||||
}
|
||||
],
|
||||
|
||||
'no-return-await': 'off',
|
||||
'@typescript-eslint/return-await': ['error', 'always'],
|
||||
'@typescript-eslint/promise-function-async': 'error',
|
||||
'@typescript-eslint/await-thenable': 'error',
|
||||
'svelte/no-at-html-tags': 'off'
|
||||
},
|
||||
settings: {
|
||||
'import-x/extensions': ['.ts'],
|
||||
'import-x/parsers': {
|
||||
'@typescript-eslint/parser': ['.ts']
|
||||
},
|
||||
'import-x/resolver': {
|
||||
typescript: {
|
||||
project: ['./tsconfig.json', './.svelte-kit/tsconfig.json']
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: {
|
||||
'import-x': pluginImportX
|
||||
}
|
||||
}
|
||||
);
|
76
ui/package.json
Normal file
76
ui/package.json
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "@gitbutler/ui",
|
||||
"version": "0.0.1",
|
||||
"description": "GitButler Component Library",
|
||||
"keywords": [
|
||||
"components",
|
||||
"documentation",
|
||||
"Svelte",
|
||||
"SvelteKit"
|
||||
],
|
||||
"type": "module",
|
||||
"engines": {
|
||||
"node": ">=20.11"
|
||||
},
|
||||
"packageManager": "pnpm@9.2.0",
|
||||
"scripts": {
|
||||
"dev": "vite dev",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"format": "prettier --write .",
|
||||
"fix": "eslint --fix .",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.json",
|
||||
"check:watch": "pnpm check --watch",
|
||||
"package": "svelte-kit sync && svelte-package && publint",
|
||||
"prepublishOnly": "npm run package",
|
||||
"prepare": "svelte-kit sync",
|
||||
"storybook": "storybook dev --no-open -p 6006"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chromatic-com/storybook": "^1.5.0",
|
||||
"@eslint/js": "^9.5.0",
|
||||
"@storybook/addon-essentials": "^8.1.10",
|
||||
"@storybook/addon-interactions": "^8.1.10",
|
||||
"@storybook/addon-links": "^8.1.10",
|
||||
"@storybook/blocks": "^8.1.10",
|
||||
"@storybook/svelte": "^8.1.10",
|
||||
"@storybook/sveltekit": "^8.1.10",
|
||||
"@storybook/test": "^8.1.10",
|
||||
"@sveltejs/adapter-static": "^3.0.1",
|
||||
"@sveltejs/kit": "^2.5.10",
|
||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/diff": "^5.2.1",
|
||||
"@types/diff-match-patch": "^1.0.36",
|
||||
"@types/eslint__js": "^8.42.3",
|
||||
"@types/git-url-parse": "^9.0.3",
|
||||
"@types/lscache": "^1.3.4",
|
||||
"@types/marked": "^5.0.2",
|
||||
"@typescript-eslint/parser": "^7.13.1",
|
||||
"eslint": "^9.5.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-import-x": "^0.5.1",
|
||||
"eslint-plugin-storybook": "^0.8.0",
|
||||
"eslint-plugin-svelte": "2.40.0",
|
||||
"globals": "^15.6.0",
|
||||
"prettier": "^3.3.2",
|
||||
"prettier-plugin-svelte": "^3.2.4",
|
||||
"storybook": "^8.1.10",
|
||||
"svelte": "5.0.0-next.149",
|
||||
"svelte-check": "^3.8.0",
|
||||
"svelte-eslint-parser": "github:gitbutlerapp/svelte-eslint-parser",
|
||||
"svelte-floating-ui": "^1.5.8",
|
||||
"svelte-french-toast": "^1.2.0",
|
||||
"svelte-loadable-store": "^2.0.1",
|
||||
"svelte-resize-observer": "^2.0.0",
|
||||
"typescript": "^5.4.5",
|
||||
"typescript-eslint": "^7.13.1",
|
||||
"vite": "^5.2.13",
|
||||
"vitest": "^0.34.6"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"plugin:storybook/recommended"
|
||||
]
|
||||
}
|
||||
}
|
13
ui/src/app.d.ts
vendored
Normal file
13
ui/src/app.d.ts
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
// See https://kit.svelte.dev/docs/types#app
|
||||
// for information about these interfaces
|
||||
declare global {
|
||||
namespace App {
|
||||
// interface Error {}
|
||||
// interface Locals {}
|
||||
// interface PageData {}
|
||||
// interface PageState {}
|
||||
// interface Platform {}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
15
ui/src/app.html
Normal file
15
ui/src/app.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<div>%sveltekit.body%</div>
|
||||
</body>
|
||||
|
||||
</html>
|
45
ui/src/lib/commitLines/BaseNode.svelte
Normal file
45
ui/src/lib/commitLines/BaseNode.svelte
Normal file
@ -0,0 +1,45 @@
|
||||
<script lang="ts">
|
||||
import type { BaseNode, Color } from '$lib/commitLines/types';
|
||||
interface Props {
|
||||
baseNode: BaseNode;
|
||||
color: Color;
|
||||
}
|
||||
|
||||
const { baseNode: _baseNode, color: _color }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<div class="node">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16" fill="none">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M4.32501 7.25C4.67247 5.53832 6.18578 4.25 8 4.25C9.81422 4.25 11.3275 5.53832 11.675 7.25H14V8.75H11.675C11.3275 10.4617 9.81422 11.75 8 11.75C6.18578 11.75 4.67247 10.4617 4.32501 8.75H2V7.25H4.32501ZM8 5.75C6.75736 5.75 5.75 6.75736 5.75 8C5.75 9.24264 6.75736 10.25 8 10.25C9.24264 10.25 10.25 9.24264 10.25 8C10.25 6.75736 9.24264 5.75 8 5.75Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.container {
|
||||
z-index: var(--z-lifted);
|
||||
|
||||
& .node {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
|
||||
margin-top: -8px;
|
||||
margin-bottom: -8px;
|
||||
margin-left: -9px;
|
||||
margin-right: -7px;
|
||||
|
||||
background-color: var(--clr-commit-remote);
|
||||
border-radius: 6px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
18
ui/src/lib/commitLines/Cell.svelte
Normal file
18
ui/src/lib/commitLines/Cell.svelte
Normal file
@ -0,0 +1,18 @@
|
||||
<script lang="ts">
|
||||
import Fork from '$lib/commitLines/Cell/Fork.svelte';
|
||||
import Straight from '$lib/commitLines/Cell/Straight.svelte';
|
||||
import type { Cell } from '$lib/commitLines/types';
|
||||
|
||||
interface Props {
|
||||
cell: Cell;
|
||||
isBottom?: boolean;
|
||||
}
|
||||
|
||||
const { cell, isBottom = false }: Props = $props();
|
||||
</script>
|
||||
|
||||
{#if cell.type === 'fork'}
|
||||
<Fork color={cell.color} style={cell.style} {isBottom} />
|
||||
{:else}
|
||||
<Straight color={cell.color} style={cell.style} />
|
||||
{/if}
|
66
ui/src/lib/commitLines/Cell/Fork.svelte
Normal file
66
ui/src/lib/commitLines/Cell/Fork.svelte
Normal file
@ -0,0 +1,66 @@
|
||||
<script lang="ts">
|
||||
import type { Color, Style } from '$lib/commitLines/types';
|
||||
|
||||
interface Props {
|
||||
color: Color;
|
||||
style?: Style;
|
||||
isBottom?: boolean;
|
||||
}
|
||||
|
||||
const { color, style, isBottom = false }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="commit-line fork"
|
||||
class:none={color === 'none'}
|
||||
class:remote={color === 'remote'}
|
||||
class:local={color === 'local'}
|
||||
class:local-and-remote={color === 'localAndRemote'}
|
||||
class:shadow={color === 'shadow'}
|
||||
class:integrated={color === 'integrated'}
|
||||
class:dashed={style === 'dashed'}
|
||||
>
|
||||
<div class="row top-row">
|
||||
<div></div>
|
||||
<div class="fork-top"></div>
|
||||
</div>
|
||||
<div class="row bottom-row" class:is-bottom={isBottom}>
|
||||
<div class="fork-bottom"></div>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.fork {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
flex-grow: 1;
|
||||
|
||||
&.bottom-row {
|
||||
height: 16px;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
&.is-bottom {
|
||||
height: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.fork-top {
|
||||
border-right: 2px var(--border-style) var(--border-color);
|
||||
border-bottom: 2px var(--border-style) var(--border-color);
|
||||
border-bottom-right-radius: 8px;
|
||||
margin-bottom: -2px;
|
||||
}
|
||||
|
||||
.fork-bottom {
|
||||
border-left: 2px var(--border-style) var(--border-color);
|
||||
border-top: 2px var(--border-style) var(--border-color);
|
||||
border-top-left-radius: 8px;
|
||||
}
|
||||
</style>
|
27
ui/src/lib/commitLines/Cell/Straight.svelte
Normal file
27
ui/src/lib/commitLines/Cell/Straight.svelte
Normal file
@ -0,0 +1,27 @@
|
||||
<script lang="ts">
|
||||
import type { Color, Style } from '$lib/commitLines/types';
|
||||
|
||||
interface Props {
|
||||
color: Color;
|
||||
style?: Style;
|
||||
}
|
||||
|
||||
const { color, style }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="commit-line straight"
|
||||
class:none={color === 'none'}
|
||||
class:remote={color === 'remote'}
|
||||
class:local={color === 'local'}
|
||||
class:local-and-remote={color === 'localAndRemote'}
|
||||
class:shadow={color === 'shadow'}
|
||||
class:integrated={color === 'integrated'}
|
||||
class:dashed={style === 'dashed'}
|
||||
></div>
|
||||
|
||||
<style lang="postcss">
|
||||
.straight {
|
||||
border-right: 2px var(--border-style) var(--border-color);
|
||||
}
|
||||
</style>
|
113
ui/src/lib/commitLines/CommitNode.svelte
Normal file
113
ui/src/lib/commitLines/CommitNode.svelte
Normal file
@ -0,0 +1,113 @@
|
||||
<script lang="ts">
|
||||
import { tooltip } from '$lib/utils/tooltip';
|
||||
import { isDefined } from '$lib/utils/typeguards';
|
||||
import type { CommitNode, Color } from '$lib/commitLines/types';
|
||||
|
||||
interface Props {
|
||||
commitNode: CommitNode;
|
||||
color: Color;
|
||||
}
|
||||
|
||||
const { commitNode, color }: Props = $props();
|
||||
|
||||
const hoverText = $derived(
|
||||
[
|
||||
commitNode.commit?.author.name,
|
||||
commitNode.commit?.title,
|
||||
commitNode.commit?.id.substring(0, 7)
|
||||
]
|
||||
.filter(isDefined)
|
||||
.join('\n')
|
||||
);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="container"
|
||||
class:none={color === 'none'}
|
||||
class:remote={color === 'remote'}
|
||||
class:local={color === 'local'}
|
||||
class:local-and-remote={color === 'localAndRemote'}
|
||||
class:shadow={color === 'shadow'}
|
||||
class:integrated={color === 'integrated'}
|
||||
>
|
||||
{#if commitNode.type === 'large' && commitNode.commit}
|
||||
<div class="large-node">
|
||||
<img
|
||||
class="avatar"
|
||||
alt="Gravatar for {commitNode.commit.author.email}"
|
||||
srcset="{commitNode.commit.author.gravatarUrl} 2x"
|
||||
width="100"
|
||||
height="100"
|
||||
use:tooltip={hoverText}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="small-node" use:tooltip={hoverText}></div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.container {
|
||||
z-index: var(--z-ground);
|
||||
|
||||
&.none {
|
||||
--border-color: transparent;
|
||||
}
|
||||
|
||||
&.remote {
|
||||
--border-color: var(--clr-commit-upstream);
|
||||
}
|
||||
|
||||
&.local {
|
||||
--border-color: var(--clr-commit-local);
|
||||
}
|
||||
|
||||
&.local-and-remote {
|
||||
--border-color: var(--clr-commit-remote);
|
||||
}
|
||||
|
||||
&.shadow {
|
||||
--border-color: var(--clr-commit-shadow);
|
||||
}
|
||||
|
||||
&.integrated {
|
||||
--border-color: var(--clr-commit-shadow);
|
||||
}
|
||||
& .small-node {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
|
||||
margin-top: -5px;
|
||||
margin-bottom: -5px;
|
||||
margin-left: -6px;
|
||||
margin-right: -4px;
|
||||
background-color: var(--border-color);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
& .large-node {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
|
||||
margin-top: -8px;
|
||||
margin-bottom: -8px;
|
||||
margin-left: -9px;
|
||||
margin-right: -7px;
|
||||
|
||||
background-color: var(--border-color);
|
||||
border-radius: 8px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
position: relative;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
57
ui/src/lib/commitLines/Line.svelte
Normal file
57
ui/src/lib/commitLines/Line.svelte
Normal file
@ -0,0 +1,57 @@
|
||||
<script lang="ts">
|
||||
import BaseNode from '$lib/commitLines/BaseNode.svelte';
|
||||
import Cell from '$lib/commitLines/Cell.svelte';
|
||||
import CommitNode from '$lib/commitLines/CommitNode.svelte';
|
||||
import { pxToRem } from '$lib/utils/pxToRem';
|
||||
import type { Line } from '$lib/commitLines/types';
|
||||
|
||||
interface Props {
|
||||
line: Line;
|
||||
topHeightPx?: number;
|
||||
}
|
||||
|
||||
const { line, topHeightPx = 24 }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="line">
|
||||
<div
|
||||
class="line-top"
|
||||
style:--top-height={pxToRem(topHeightPx)}
|
||||
class:has-branch-node={line.baseNode}
|
||||
>
|
||||
<Cell cell={line.top} />
|
||||
</div>
|
||||
{#if line.commitNode}
|
||||
<CommitNode commitNode={line.commitNode} color={line.bottom.color} />
|
||||
{:else if line.baseNode}
|
||||
<BaseNode baseNode={line.baseNode} color={line.top.color} />
|
||||
{/if}
|
||||
<div class="line-bottom">
|
||||
<Cell cell={line.bottom} isBottom />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.line {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
width: 24px;
|
||||
margin-right: -2px;
|
||||
}
|
||||
|
||||
.line-top {
|
||||
height: var(--top-height);
|
||||
width: 100%;
|
||||
|
||||
&.has-branch-node {
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.line-bottom {
|
||||
flex-grow: 1;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
24
ui/src/lib/commitLines/LineGroup.svelte
Normal file
24
ui/src/lib/commitLines/LineGroup.svelte
Normal file
@ -0,0 +1,24 @@
|
||||
<script lang="ts">
|
||||
import Line from '$lib/commitLines/Line.svelte';
|
||||
import type { LineGroup } from '$lib/commitLines/types';
|
||||
|
||||
interface Props {
|
||||
lineGroup: LineGroup;
|
||||
topHeightPx?: number;
|
||||
}
|
||||
|
||||
const { lineGroup, topHeightPx }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="line-group">
|
||||
{#each lineGroup.lines as line}
|
||||
<Line {line} {topHeightPx} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
.line-group {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
489
ui/src/lib/commitLines/lineManager.ts
Normal file
489
ui/src/lib/commitLines/lineManager.ts
Normal file
@ -0,0 +1,489 @@
|
||||
import type { CommitData, LineGroup, Line, Color } from '$lib/commitLines/types';
|
||||
|
||||
interface Commits {
|
||||
remoteCommits: CommitData[];
|
||||
localCommits: CommitData[];
|
||||
localAndRemoteCommits: CommitData[];
|
||||
integratedCommits: CommitData[];
|
||||
}
|
||||
|
||||
function generateSameForkpoint({
|
||||
remoteCommits,
|
||||
localCommits,
|
||||
localAndRemoteCommits,
|
||||
integratedCommits
|
||||
}: Commits) {
|
||||
const LEFT_COLUMN_INDEX = 0;
|
||||
const RIGHT_COLUMN_INDEX = 1;
|
||||
|
||||
const remoteBranchGroups = mapToCommitLineGroupPair(remoteCommits, 3);
|
||||
const localBranchGroups = mapToCommitLineGroupPair(localCommits, 3);
|
||||
const localAndRemoteBranchGroups = mapToCommitLineGroupPair(localAndRemoteCommits, 3);
|
||||
const integratedBranchGroups = mapToCommitLineGroupPair(integratedCommits, 3);
|
||||
|
||||
const base = blankLineGroup(3);
|
||||
|
||||
remoteBranchGroups.forEach(({ commit, lineGroup }, index) => {
|
||||
// We don't color in top half of the first remote commit
|
||||
if (index !== 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'remote';
|
||||
}
|
||||
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'remote';
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = { type: 'large', commit };
|
||||
|
||||
// If there are local commits we want to fill in a local dashed line
|
||||
if (localBranchGroups.length > 0) {
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.color = 'local';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.color = 'local';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.style = 'dashed';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.style = 'dashed';
|
||||
}
|
||||
});
|
||||
|
||||
let localCommitWithChangeIdFound = false;
|
||||
localBranchGroups.forEach(({ commit, lineGroup }, index) => {
|
||||
// The first local commit should have the top be dashed
|
||||
if (index === 0) {
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.style = 'dashed';
|
||||
}
|
||||
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.color = 'local';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.color = 'local';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].commitNode = { type: 'large', commit };
|
||||
|
||||
// We need to use either remote or localAndRemote depending on what is above
|
||||
let leftStyle: Color | undefined;
|
||||
|
||||
if (remoteBranchGroups.length > 0) {
|
||||
leftStyle = 'remote';
|
||||
} else {
|
||||
leftStyle = 'localAndRemote';
|
||||
}
|
||||
|
||||
if (localCommitWithChangeIdFound) {
|
||||
// If a commit with a change ID has been found above this commit, use the leftStyle
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = leftStyle;
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = leftStyle;
|
||||
|
||||
if (commit.relatedRemoteCommit) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = {
|
||||
type: 'small',
|
||||
commit: commit.relatedRemoteCommit
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (commit.relatedRemoteCommit) {
|
||||
// For the first commit with a change ID found, only set the top if there are any remote commits
|
||||
if (remoteBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = leftStyle;
|
||||
}
|
||||
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = {
|
||||
type: 'small',
|
||||
commit: commit.relatedRemoteCommit
|
||||
};
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = leftStyle;
|
||||
|
||||
localCommitWithChangeIdFound = true;
|
||||
} else {
|
||||
// If there are any remote commits, continue the line
|
||||
if (remoteBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'remote';
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'remote';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
localAndRemoteBranchGroups.forEach(({ commit, lineGroup }, index) => {
|
||||
if (index === 0) {
|
||||
// Copy the top color from any commits above for the first commit
|
||||
if (localBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color =
|
||||
localBranchGroups.at(-1)!.lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color;
|
||||
} else if (remoteBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color =
|
||||
remoteBranchGroups.at(-1)!.lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color;
|
||||
}
|
||||
} else {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'localAndRemote';
|
||||
}
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'localAndRemote';
|
||||
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = { type: 'large', commit };
|
||||
});
|
||||
|
||||
integratedBranchGroups.forEach(({ commit, lineGroup }, index) => {
|
||||
if (index === 0) {
|
||||
// Copy the top color from any commits above for the first commit
|
||||
if (localAndRemoteBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color =
|
||||
localAndRemoteBranchGroups.at(-1)!.lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color;
|
||||
} else if (localBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color =
|
||||
localBranchGroups.at(-1)!.lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color;
|
||||
} else if (remoteBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color =
|
||||
remoteBranchGroups.at(-1)!.lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color;
|
||||
}
|
||||
} else {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'integrated';
|
||||
}
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'integrated';
|
||||
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = { type: 'large', commit };
|
||||
});
|
||||
|
||||
// Set forkpoints
|
||||
if (localBranchGroups.length > 0) {
|
||||
if (integratedBranchGroups.length === 0 && localAndRemoteBranchGroups.length === 0) {
|
||||
localBranchGroups.at(-1)!.lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.type = 'fork';
|
||||
} else if (localAndRemoteBranchGroups.length > 0) {
|
||||
localAndRemoteBranchGroups[0].lineGroup.lines[RIGHT_COLUMN_INDEX].top.type = 'fork';
|
||||
localAndRemoteBranchGroups[0].lineGroup.lines[RIGHT_COLUMN_INDEX].top.color = 'local';
|
||||
} else if (integratedBranchGroups.length > 0) {
|
||||
integratedBranchGroups[0].lineGroup.lines[RIGHT_COLUMN_INDEX].top.type = 'fork';
|
||||
integratedBranchGroups[0].lineGroup.lines[RIGHT_COLUMN_INDEX].top.color = 'local';
|
||||
}
|
||||
}
|
||||
|
||||
// Remove padding column if unrequired
|
||||
if (localBranchGroups.length === 0) {
|
||||
remoteBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.pop());
|
||||
localBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.pop());
|
||||
localAndRemoteBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.pop());
|
||||
integratedBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.pop());
|
||||
|
||||
base.lines.pop();
|
||||
}
|
||||
|
||||
// Set base
|
||||
base.lines[LEFT_COLUMN_INDEX].baseNode = {};
|
||||
base.lines[LEFT_COLUMN_INDEX].top.style = 'dashed';
|
||||
if (integratedBranchGroups.length > 0) {
|
||||
base.lines[LEFT_COLUMN_INDEX].top.color = 'integrated';
|
||||
} else if (localAndRemoteBranchGroups.length > 0) {
|
||||
base.lines[LEFT_COLUMN_INDEX].top.color = 'localAndRemote';
|
||||
} else if (localBranchGroups.length > 0) {
|
||||
base.lines[LEFT_COLUMN_INDEX].top.color = 'local';
|
||||
} else if (remoteBranchGroups.length > 0) {
|
||||
base.lines[LEFT_COLUMN_INDEX].top.color = 'remote';
|
||||
} else {
|
||||
base.lines[LEFT_COLUMN_INDEX].baseNode = undefined;
|
||||
}
|
||||
|
||||
const data = new Map<string, LineGroup>([
|
||||
...remoteBranchGroups.map(({ commit, lineGroup }) => [commit.id, lineGroup]),
|
||||
...localBranchGroups.map(({ commit, lineGroup }) => [commit.id, lineGroup]),
|
||||
...localAndRemoteBranchGroups.map(({ commit, lineGroup }) => [commit.id, lineGroup]),
|
||||
...integratedBranchGroups.map(({ commit, lineGroup }) => [commit.id, lineGroup])
|
||||
] as [string, LineGroup][]);
|
||||
|
||||
return { data, base };
|
||||
}
|
||||
|
||||
function generateDifferentForkpoint({
|
||||
remoteCommits,
|
||||
localCommits,
|
||||
localAndRemoteCommits,
|
||||
integratedCommits
|
||||
}: Commits) {
|
||||
const LEFT_COLUMN_INDEX = 0;
|
||||
const MIDDLE_COLUMN_INDEX = 1;
|
||||
const RIGHT_COLUMN_INDEX = 2;
|
||||
|
||||
if (localAndRemoteCommits.length > 0) {
|
||||
throw new Error('There should never be local and remote commits with a different forkpoint');
|
||||
}
|
||||
|
||||
const remoteBranchGroups = mapToCommitLineGroupPair(remoteCommits, 4);
|
||||
const localBranchGroups = mapToCommitLineGroupPair(localCommits, 4);
|
||||
const integratedBranchGroups = mapToCommitLineGroupPair(integratedCommits, 4);
|
||||
|
||||
const base = blankLineGroup(4);
|
||||
|
||||
remoteBranchGroups.forEach(({ commit, lineGroup }, index) => {
|
||||
// Don't color top half if its the first commit of the list
|
||||
if (index !== 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'remote';
|
||||
}
|
||||
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'remote';
|
||||
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = { type: 'large', commit };
|
||||
|
||||
// If there are local commits further down, render a dashed line from the top of the list
|
||||
if (localBranchGroups.length > 0) {
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.color = 'local';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.color = 'local';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.style = 'dashed';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.style = 'dashed';
|
||||
}
|
||||
});
|
||||
|
||||
let localCommitWithChangeIdFound = false;
|
||||
localBranchGroups.forEach(({ commit, lineGroup }, index) => {
|
||||
// Make the top commit dashed
|
||||
if (index === 0) {
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.style = 'dashed';
|
||||
}
|
||||
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.color = 'local';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.color = 'local';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].commitNode = { type: 'large', commit };
|
||||
|
||||
if (localCommitWithChangeIdFound) {
|
||||
// If a previous local commit with change ID was found, color with "shadow"
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'shadow';
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'shadow';
|
||||
|
||||
if (commit.relatedRemoteCommit) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = {
|
||||
type: 'small',
|
||||
commit: commit.relatedRemoteCommit
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (commit.relatedRemoteCommit) {
|
||||
// If the commit has a commit with a matching change ID, mark it in the shadow lane
|
||||
|
||||
// Since this is the first, if there were any remote commits, we should inherit that color
|
||||
if (remoteBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'remote';
|
||||
}
|
||||
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = {
|
||||
type: 'small',
|
||||
commit: commit.relatedRemoteCommit
|
||||
};
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'shadow';
|
||||
|
||||
localCommitWithChangeIdFound = true;
|
||||
} else {
|
||||
// Otherwise maintain the left color if it exists
|
||||
if (remoteBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'remote';
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'remote';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
integratedBranchGroups.forEach(({ commit, lineGroup }, index) => {
|
||||
if (localBranchGroups.length === 0 && remoteBranchGroups.length === 0) {
|
||||
// If there are no local or remote branches, we want to have a single dashed line
|
||||
|
||||
// Don't color in if its the first commit
|
||||
if (index !== 0) {
|
||||
lineGroup.lines[MIDDLE_COLUMN_INDEX].top.color = 'integrated';
|
||||
lineGroup.lines[MIDDLE_COLUMN_INDEX].top.style = 'dashed';
|
||||
}
|
||||
|
||||
lineGroup.lines[MIDDLE_COLUMN_INDEX].bottom.color = 'integrated';
|
||||
lineGroup.lines[MIDDLE_COLUMN_INDEX].bottom.style = 'dashed';
|
||||
|
||||
lineGroup.lines[MIDDLE_COLUMN_INDEX].commitNode = { type: 'large', commit };
|
||||
} else {
|
||||
// If we have local branches, maintain that color on the top half of the first commit
|
||||
if (index === 0) {
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.color =
|
||||
localBranchGroups.at(-1)?.lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.color || 'none';
|
||||
} else {
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].top.color = 'integrated';
|
||||
}
|
||||
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.color = 'integrated';
|
||||
lineGroup.lines[RIGHT_COLUMN_INDEX].commitNode = { type: 'large', commit };
|
||||
|
||||
if (localCommitWithChangeIdFound) {
|
||||
// If there is a commit with change id above, just use shadow style
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'shadow';
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'shadow';
|
||||
|
||||
if (commit.relatedRemoteCommit) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = {
|
||||
type: 'small',
|
||||
commit: commit.relatedRemoteCommit
|
||||
};
|
||||
}
|
||||
} else {
|
||||
if (commit.relatedRemoteCommit) {
|
||||
// If we have just found a commit with a shadow style, match the top style
|
||||
if (remoteBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'remote';
|
||||
}
|
||||
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].commitNode = {
|
||||
type: 'small',
|
||||
commit: commit.relatedRemoteCommit
|
||||
};
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'shadow';
|
||||
|
||||
localCommitWithChangeIdFound = true;
|
||||
} else {
|
||||
// Otherwise style as remote if there are any
|
||||
if (remoteBranchGroups.length > 0) {
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color = 'remote';
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color = 'remote';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Mark the fork point below the integrated or local branch groups if present
|
||||
if (integratedBranchGroups.length > 0) {
|
||||
integratedBranchGroups.at(-1)!.lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.type = 'fork';
|
||||
} else if (localBranchGroups.length > 0) {
|
||||
localBranchGroups.at(-1)!.lineGroup.lines[RIGHT_COLUMN_INDEX].bottom.type = 'fork';
|
||||
}
|
||||
|
||||
function setLeftSideBase() {
|
||||
let color: Color | undefined;
|
||||
if (integratedBranchGroups.length > 0) {
|
||||
color = integratedBranchGroups.at(-1)!.lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color;
|
||||
} else if (localBranchGroups.length > 0) {
|
||||
color = localBranchGroups.at(-1)!.lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color;
|
||||
} else if (remoteBranchGroups.length > 0) {
|
||||
color = remoteBranchGroups.at(-1)!.lineGroup.lines[LEFT_COLUMN_INDEX].bottom.color;
|
||||
} else {
|
||||
color = 'none';
|
||||
}
|
||||
|
||||
base.lines[LEFT_COLUMN_INDEX].top.color = color;
|
||||
base.lines[LEFT_COLUMN_INDEX].bottom.color = color;
|
||||
base.lines[LEFT_COLUMN_INDEX].top.style = 'dashed';
|
||||
base.lines[LEFT_COLUMN_INDEX].bottom.style = 'dashed';
|
||||
}
|
||||
|
||||
// Set base
|
||||
if (integratedBranchGroups.length > 0) {
|
||||
base.lines[MIDDLE_COLUMN_INDEX].top.color = 'integrated';
|
||||
base.lines[MIDDLE_COLUMN_INDEX].top.style =
|
||||
integratedBranchGroups.at(-1)!.lineGroup.lines[MIDDLE_COLUMN_INDEX].bottom.style;
|
||||
base.lines[MIDDLE_COLUMN_INDEX].baseNode = {};
|
||||
|
||||
setLeftSideBase();
|
||||
} else if (localBranchGroups.length > 0) {
|
||||
base.lines[MIDDLE_COLUMN_INDEX].top.color = 'local';
|
||||
base.lines[MIDDLE_COLUMN_INDEX].baseNode = {};
|
||||
|
||||
setLeftSideBase();
|
||||
} else if (remoteBranchGroups.length > 0) {
|
||||
base.lines[LEFT_COLUMN_INDEX].top.color = 'remote';
|
||||
base.lines[LEFT_COLUMN_INDEX].top.style = 'dashed';
|
||||
base.lines[LEFT_COLUMN_INDEX].baseNode = {};
|
||||
}
|
||||
|
||||
function removeLeftMostColumn() {
|
||||
remoteBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.shift());
|
||||
localBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.shift());
|
||||
integratedBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.shift());
|
||||
base.lines.shift();
|
||||
}
|
||||
|
||||
function removeRightMostColumn() {
|
||||
remoteBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.pop());
|
||||
localBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.pop());
|
||||
integratedBranchGroups.forEach(({ lineGroup }) => lineGroup.lines.pop());
|
||||
base.lines.pop();
|
||||
}
|
||||
|
||||
// Remove the left column if there is no ghost line
|
||||
const hasGhostLine = [
|
||||
...remoteBranchGroups,
|
||||
...localBranchGroups,
|
||||
...integratedBranchGroups
|
||||
].some(
|
||||
({ lineGroup }) =>
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color !== 'none' ||
|
||||
lineGroup.lines[LEFT_COLUMN_INDEX].top.color !== 'none'
|
||||
);
|
||||
|
||||
if (!hasGhostLine) {
|
||||
removeLeftMostColumn();
|
||||
}
|
||||
|
||||
// Remove the right two columns if there is only remote commits
|
||||
if (integratedBranchGroups.length === 0 && localBranchGroups.length === 0) {
|
||||
removeRightMostColumn();
|
||||
removeRightMostColumn();
|
||||
}
|
||||
|
||||
// Remove one right column if there is only integrated with no local or remote commits
|
||||
if (integratedBranchGroups.length > 0 && localBranchGroups.length === 0 && remoteBranchGroups) {
|
||||
removeRightMostColumn();
|
||||
}
|
||||
|
||||
const data = new Map<string, LineGroup>([
|
||||
...remoteBranchGroups.map(({ commit, lineGroup }) => [commit.id, lineGroup]),
|
||||
...localBranchGroups.map(({ commit, lineGroup }) => [commit.id, lineGroup]),
|
||||
...integratedBranchGroups.map(({ commit, lineGroup }) => [commit.id, lineGroup])
|
||||
] as [string, LineGroup][]);
|
||||
|
||||
return { data, base };
|
||||
}
|
||||
|
||||
function mapToCommitLineGroupPair(commits: CommitData[], groupSize: number) {
|
||||
const groupings = commits.map((commit) => ({
|
||||
commit,
|
||||
lineGroup: blankLineGroup(groupSize)
|
||||
}));
|
||||
|
||||
return groupings;
|
||||
}
|
||||
|
||||
function blankLineGroup(lineCount: number): LineGroup {
|
||||
const lines = Array(lineCount)
|
||||
.fill(undefined)
|
||||
.map(
|
||||
(): Line => ({
|
||||
top: { type: 'straight', color: 'none' },
|
||||
bottom: { type: 'straight', color: 'none' }
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
lines
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The Line Manager assumes that the groups of commits will be in the following order:
|
||||
* 1. Remote Commits (Commits you don't have in your branch)
|
||||
* 2. Local Commits (Commits that you have changed locally)
|
||||
* 3. LocalAndRemote Commits (Commits that exist locally and on the remote and have the same hash)
|
||||
* 4. Integrated Commits (Commits that exist locally and perhaps on the remote that are in the trunk)
|
||||
*/
|
||||
export class LineManager {
|
||||
private data: Map<string, LineGroup>;
|
||||
base: LineGroup;
|
||||
|
||||
constructor(commits: Commits, sameForkpoint: boolean) {
|
||||
// We should never have local and remote commits with a different forkpoint
|
||||
if (sameForkpoint || commits.localAndRemoteCommits.length > 0) {
|
||||
const { data, base } = generateSameForkpoint(commits);
|
||||
this.data = data;
|
||||
this.base = base;
|
||||
} else {
|
||||
const { data, base } = generateDifferentForkpoint(commits);
|
||||
this.data = data;
|
||||
this.base = base;
|
||||
}
|
||||
}
|
||||
|
||||
get(commitId: string) {
|
||||
if (!this.data.has(commitId)) {
|
||||
throw new Error(`Failed to find commit ${commitId} in line manager`);
|
||||
}
|
||||
|
||||
return this.data.get(commitId)!;
|
||||
}
|
||||
}
|
||||
|
||||
export class LineManagerFactory {
|
||||
build(commits: Commits, sameForkpoint: boolean) {
|
||||
return new LineManager(commits, sameForkpoint);
|
||||
}
|
||||
}
|
26
ui/src/lib/commitLines/transformers.ts
Normal file
26
ui/src/lib/commitLines/transformers.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import type { CommitData, Author } from '$lib/commitLines/types';
|
||||
import type { AnyCommit } from '$lib/vbranches/types';
|
||||
|
||||
export function transformAnyCommit(anyCommit: AnyCommit): CommitData {
|
||||
const output = pullCommitDetails(anyCommit);
|
||||
|
||||
if (anyCommit.relatedTo) {
|
||||
output.relatedRemoteCommit = pullCommitDetails(anyCommit.relatedTo);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function pullCommitDetails(anyCommit: AnyCommit): CommitData {
|
||||
const author: Author = {
|
||||
name: anyCommit.author.name,
|
||||
email: anyCommit.author.email,
|
||||
gravatarUrl: anyCommit.author.gravatarUrl
|
||||
};
|
||||
|
||||
return {
|
||||
id: anyCommit.id,
|
||||
title: anyCommit.descriptionTitle,
|
||||
author
|
||||
};
|
||||
}
|
43
ui/src/lib/commitLines/types.ts
Normal file
43
ui/src/lib/commitLines/types.ts
Normal file
@ -0,0 +1,43 @@
|
||||
export type Color = 'local' | 'localAndRemote' | 'remote' | 'integrated' | 'shadow' | 'none';
|
||||
|
||||
export type Style = 'dashed';
|
||||
export interface Cell {
|
||||
type: 'straight' | 'fork';
|
||||
color: Color;
|
||||
style?: Style;
|
||||
}
|
||||
|
||||
export interface CommitNode {
|
||||
type: 'small' | 'large';
|
||||
commit?: CommitData;
|
||||
}
|
||||
|
||||
export interface BaseNode {}
|
||||
|
||||
export interface Line {
|
||||
top: Cell;
|
||||
bottom: Cell;
|
||||
commitNode?: CommitNode;
|
||||
baseNode?: BaseNode;
|
||||
}
|
||||
|
||||
export interface LineGroup {
|
||||
// A tuple of two, three, or four lines
|
||||
lines: Line[];
|
||||
}
|
||||
|
||||
export interface Author {
|
||||
name?: string;
|
||||
email?: string;
|
||||
gravatarUrl?: URL;
|
||||
}
|
||||
|
||||
/**
|
||||
* A minimal set of data required to represent a commit for line drawing purpouses
|
||||
*/
|
||||
export interface CommitData {
|
||||
id: string;
|
||||
title?: string;
|
||||
author: Author;
|
||||
relatedRemoteCommit?: CommitData;
|
||||
}
|
4
ui/src/lib/utils/pxToRem.ts
Normal file
4
ui/src/lib/utils/pxToRem.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export function pxToRem(px: number | undefined, base: number = 16) {
|
||||
if (!px) return;
|
||||
return `${px / base}rem`;
|
||||
}
|
119
ui/src/lib/utils/tooltip.ts
Normal file
119
ui/src/lib/utils/tooltip.ts
Normal file
@ -0,0 +1,119 @@
|
||||
export interface ToolTipOptions {
|
||||
text: string;
|
||||
noMaxWidth?: boolean;
|
||||
delay?: number;
|
||||
}
|
||||
|
||||
const defaultOptions: Partial<ToolTipOptions> = {
|
||||
delay: 1200,
|
||||
noMaxWidth: false
|
||||
};
|
||||
|
||||
export function tooltip(node: HTMLElement, optsOrString: ToolTipOptions | string | undefined) {
|
||||
// The tooltip element we are adding to the dom
|
||||
let tooltip: HTMLDivElement | undefined;
|
||||
|
||||
// Note that we use this both for delaying show, as well as delaying hide
|
||||
let timeoutId: any;
|
||||
|
||||
// Options
|
||||
let { text, delay, noMaxWidth } = defaultOptions;
|
||||
|
||||
// Most use cases only involve passing a string, so we allow either opts of
|
||||
// simple text.
|
||||
function setOpts(opts: ToolTipOptions | string | undefined) {
|
||||
if (typeof opts === 'string') {
|
||||
text = opts;
|
||||
} else if (opts) {
|
||||
({ text, delay, noMaxWidth } = opts || {});
|
||||
}
|
||||
if (tooltip && text) tooltip.innerText = text;
|
||||
}
|
||||
|
||||
setOpts(optsOrString);
|
||||
|
||||
function onMouseOver() {
|
||||
// If tooltip is displayed we clear hide timeout
|
||||
if (tooltip && timeoutId) clearTimeout(timeoutId);
|
||||
// If no tooltip and no timeout id we set a show timeout
|
||||
else if (!tooltip && !timeoutId) timeoutId = setTimeout(() => show(), delay);
|
||||
}
|
||||
|
||||
function onMouseLeave() {
|
||||
// If tooltip shown when mouse out then we hide after delay
|
||||
if (tooltip) hide();
|
||||
// But if we mouse out before tooltip is shown, we cancel the show timer
|
||||
else if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function show() {
|
||||
if (!text || !node.isConnected) return;
|
||||
tooltip = document.createElement('div') as HTMLDivElement;
|
||||
// TODO: Can we co-locate tooltip.js & tooltip.postcss?
|
||||
tooltip.classList.add('tooltip', 'text-base-11'); // see tooltip.postcss
|
||||
if (noMaxWidth) tooltip.classList.add('no-max-width');
|
||||
tooltip.innerText = text;
|
||||
document.body.appendChild(tooltip);
|
||||
adjustPosition();
|
||||
}
|
||||
|
||||
function hide() {
|
||||
if (tooltip) tooltip.remove();
|
||||
tooltip = undefined;
|
||||
timeoutId = undefined;
|
||||
}
|
||||
|
||||
function adjustPosition() {
|
||||
if (!tooltip) return;
|
||||
|
||||
// Dimensions and position of target element
|
||||
const nodeRect = node.getBoundingClientRect();
|
||||
const nodeHeight = nodeRect.height;
|
||||
const nodeWidth = nodeRect.width;
|
||||
const nodeLeft = nodeRect.left;
|
||||
const nodeTop = nodeRect.top;
|
||||
|
||||
// Padding
|
||||
const padding = 4;
|
||||
|
||||
// Window dimensions
|
||||
const windowHeight = window.innerHeight;
|
||||
const windowWidth = window.innerWidth;
|
||||
|
||||
const tooltipHeight = tooltip.offsetHeight;
|
||||
const tooltipWidth = tooltip.offsetWidth;
|
||||
|
||||
const showBelow = windowHeight > nodeTop + nodeHeight + tooltipHeight + padding;
|
||||
|
||||
// Note that we don't check if width of tooltip is wider than the window.
|
||||
|
||||
if (showBelow) {
|
||||
tooltip.style.top = `${nodeTop + nodeHeight + padding}px`;
|
||||
} else {
|
||||
tooltip.style.top = `${nodeTop - tooltipHeight - padding}px`;
|
||||
}
|
||||
|
||||
let leftPos = nodeLeft - (tooltipWidth - nodeWidth) / 2;
|
||||
if (leftPos < padding) leftPos = padding;
|
||||
if (leftPos + tooltipWidth > windowWidth) leftPos = windowWidth - tooltipWidth - padding;
|
||||
tooltip.style.left = `${leftPos}px`;
|
||||
}
|
||||
|
||||
node.addEventListener('mouseover', onMouseOver);
|
||||
node.addEventListener('mouseleave', onMouseLeave);
|
||||
|
||||
return {
|
||||
update(opts: ToolTipOptions | string | undefined) {
|
||||
setOpts(opts);
|
||||
},
|
||||
destroy() {
|
||||
tooltip?.remove();
|
||||
timeoutId && clearTimeout(timeoutId);
|
||||
node.removeEventListener('mouseover', onMouseOver);
|
||||
node.removeEventListener('mouseleave', onMouseLeave);
|
||||
}
|
||||
};
|
||||
}
|
23
ui/src/lib/utils/typeguards.ts
Normal file
23
ui/src/lib/utils/typeguards.ts
Normal file
@ -0,0 +1,23 @@
|
||||
export function isDefined<T>(file: T | undefined | null): file is T {
|
||||
return file !== undefined;
|
||||
}
|
||||
|
||||
export function notNull<T>(file: T | undefined | null): file is T {
|
||||
return file !== null;
|
||||
}
|
||||
|
||||
export type UnknownObject = Record<string, unknown>;
|
||||
|
||||
/**
|
||||
* Checks if the provided value is a non-empty object.
|
||||
* @param something - The value to be checked.
|
||||
* @returns A boolean indicating whether the value is a non-empty object.
|
||||
*/
|
||||
export function isNonEmptyObject(something: unknown): something is UnknownObject {
|
||||
return (
|
||||
typeof something === 'object' &&
|
||||
something !== null &&
|
||||
!Array.isArray(something) &&
|
||||
(Object.keys(something).length > 0 || Object.getOwnPropertySymbols(something).length > 0)
|
||||
);
|
||||
}
|
3
ui/src/routes/+page.svelte
Normal file
3
ui/src/routes/+page.svelte
Normal file
@ -0,0 +1,3 @@
|
||||
<h1>Welcome to your library project</h1>
|
||||
<p>Create your package using @sveltejs/package and preview/showcase your work with SvelteKit</p>
|
||||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
33
ui/src/styles/card.css
Normal file
33
ui/src/styles/card.css
Normal file
@ -0,0 +1,33 @@
|
||||
.card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border: 1px solid var(--clr-border-2);
|
||||
border-radius: var(--radius-m);
|
||||
background: var(--clr-bg-1);
|
||||
}
|
||||
|
||||
.card__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 16px;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid var(--clr-border-2);
|
||||
}
|
||||
|
||||
.card__title {
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.card__content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.card__footer {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding: 16px;
|
||||
justify-content: space-between;
|
||||
border-top: 1px solid var(--clr-border-2);
|
||||
}
|
53
ui/src/styles/commit-lines.css
Normal file
53
ui/src/styles/commit-lines.css
Normal file
@ -0,0 +1,53 @@
|
||||
.commit-line {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
--border-color: var(--clr-commit-shadow);
|
||||
--border-style: solid;
|
||||
--border-dashed: dashed;
|
||||
|
||||
&.none {
|
||||
--border-color: transparent;
|
||||
}
|
||||
|
||||
&.remote {
|
||||
--border-color: var(--clr-commit-upstream);
|
||||
}
|
||||
|
||||
&.local {
|
||||
--border-color: var(--clr-commit-local);
|
||||
}
|
||||
|
||||
&.local-and-remote {
|
||||
--border-color: var(--clr-commit-remote);
|
||||
}
|
||||
|
||||
&.shadow {
|
||||
--border-color: var(--clr-commit-shadow);
|
||||
}
|
||||
|
||||
&.integrated {
|
||||
--border-color: var(--clr-commit-shadow);
|
||||
}
|
||||
|
||||
&.dashed {
|
||||
--border-style: none;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 2px;
|
||||
height: calc(100% + 1px);
|
||||
background: repeating-linear-gradient(
|
||||
0,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
var(--border-color) 2px,
|
||||
var(--border-color) 4px
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
47
ui/src/styles/diff.css
Normal file
47
ui/src/styles/diff.css
Normal file
@ -0,0 +1,47 @@
|
||||
:root {
|
||||
--hunk-line-selected-bg: #60a5fa;
|
||||
--hunk-line-selected-border: #2563eb;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
--hunk-line-selected-bg: #044289;
|
||||
--hunk-line-selected-border: #005cc5;
|
||||
}
|
||||
|
||||
.inner-diff {
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.diff-line-marker-addition,
|
||||
.diff-line-addition {
|
||||
--override-addition-background-color: hsl(144deg 55% 49% / 20%);
|
||||
|
||||
background-color: var(--override-addition-background-color);
|
||||
}
|
||||
|
||||
.diff-line-marker-deletion,
|
||||
.diff-line-deletion {
|
||||
--override-deletion-background-color: rgba(220, 38, 38, 0.2);
|
||||
|
||||
background-color: var(--override-deletion-background-color);
|
||||
}
|
||||
|
||||
.diff-line-addition .inner-diff {
|
||||
--override-addition-inner-diff-background-color: hsl(144deg 55% 49% / 60%);
|
||||
|
||||
background-color: var(--override-addition-inner-diff-background-color);
|
||||
}
|
||||
|
||||
.diff-line-deletion .inner-diff {
|
||||
--override-deletion-inner-diff-background-color: rgba(220, 38, 38, 0.3);
|
||||
|
||||
background-color: var(--override-deletion-inner-diff-background-color);
|
||||
}
|
||||
|
||||
.diff-line-spacer {
|
||||
color: rgb(229, 231, 235);
|
||||
text-align: left;
|
||||
background-color: rgba(24, 24, 27, 1);
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
37
ui/src/styles/fonts.css
Normal file
37
ui/src/styles/fonts.css
Normal file
@ -0,0 +1,37 @@
|
||||
@font-face {
|
||||
font-family: 'PP Editorial New';
|
||||
src: url('/fonts/PPEditorialNew-Regular.woff2') format('woff2');
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Spline Sans Mono';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
src: url('/fonts/SplineSansMono-Regular.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
|
||||
U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
|
||||
U+FFFD;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Spline Sans Mono';
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
src: url('/fonts/SplineSansMono-Medium.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
|
||||
U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
|
||||
U+FFFD;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Spline Sans Mono';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-display: swap;
|
||||
src: url('/fonts/SplineSansMono-Semibold.woff2') format('woff2');
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304,
|
||||
U+0308, U+0329, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF,
|
||||
U+FFFD;
|
||||
}
|
204
ui/src/styles/main.css
Normal file
204
ui/src/styles/main.css
Normal file
@ -0,0 +1,204 @@
|
||||
@layer reset;
|
||||
|
||||
@import './reset.css';
|
||||
@import './fonts.css';
|
||||
@import './diff.css';
|
||||
@import './syntax-highlighting.css';
|
||||
@import './tokens.css';
|
||||
@import './text-classes.css';
|
||||
@import './card.css';
|
||||
@import './tooltip.css';
|
||||
@import './text-input.css';
|
||||
@import './commit-lines.css';
|
||||
@import './markdown.css';
|
||||
|
||||
/* CSS VARIABLES */
|
||||
:root {
|
||||
--transition-fast: 0.06s ease-in-out;
|
||||
--transition-medium: 0.15s ease-in-out;
|
||||
--transition-slow: 0.2s ease-in-out;
|
||||
|
||||
/* Z-index */
|
||||
--z-ground: 1;
|
||||
--z-lifted: 2;
|
||||
--z-floating: 3;
|
||||
--z-modal: 4;
|
||||
--z-tooltip: 9;
|
||||
--z-blocker: 10;
|
||||
|
||||
/* TODO: add focus color */
|
||||
--focus-color: var(--clr-scale-pop-50);
|
||||
--resizer-color: var(--clr-scale-pop-50);
|
||||
}
|
||||
|
||||
/* scrollbar helpers */
|
||||
.hide-native-scrollbar {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.truncate {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* custom scrollbar */
|
||||
.scrollbar,
|
||||
pre {
|
||||
&::-webkit-scrollbar {
|
||||
background-color: transaparent;
|
||||
width: 14px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background-color: transaparent;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: var(--clr-border-1);
|
||||
background-clip: padding-box;
|
||||
border-radius: 12px;
|
||||
border: 4px solid rgba(0, 0, 0, 0);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
text-decoration: underline;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prevents elements within drop-zones from firing mouse events, making
|
||||
* it much easier to manage in/out/over/leave events since they fire less
|
||||
* frequently.
|
||||
*/
|
||||
.drop-zone-hover * {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* FOCUS STATE */
|
||||
|
||||
.focus-state {
|
||||
&:focus-within {
|
||||
outline: 1px solid transaparent;
|
||||
animation: focus-animation var(--transition-fast) forwards;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes focus-animation {
|
||||
0% {
|
||||
outline-offset: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
outline-offset: 2px;
|
||||
outline: 1px solid var(--focus-color);
|
||||
}
|
||||
}
|
||||
|
||||
/* CODE */
|
||||
.code-string {
|
||||
font-family: 'Spline Sans Mono', monospace;
|
||||
border-radius: var(--radius-s);
|
||||
background: oklch(from var(--clr-scale-ntrl-50) l c h / 0.2);
|
||||
padding: 2px 4px;
|
||||
}
|
||||
|
||||
/* TRANSITION ANIMATION */
|
||||
|
||||
.transition-fly {
|
||||
animation: transition-fly 0.25s forwards ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes transition-fly {
|
||||
0% {
|
||||
transform: translateY(6px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* STATES */
|
||||
|
||||
.wiggle {
|
||||
animation: wiggle-animation 0.35s forwards;
|
||||
}
|
||||
|
||||
@keyframes wiggle-animation {
|
||||
0% {
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateX(3px);
|
||||
}
|
||||
|
||||
50% {
|
||||
transform: translateX(-2px);
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translateX(2px);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.locked-file-animation {
|
||||
--locked-color: oklch(from var(--clr-scale-warn-50) l c h / 0.2);
|
||||
border: 1px solid var(--clr-bg-1);
|
||||
animation: locked-file-animation 1.4s ease-out forwards;
|
||||
}
|
||||
|
||||
@keyframes locked-file-animation {
|
||||
0% {
|
||||
transform: translateX(-3px);
|
||||
background-color: var(--clr-bg-1);
|
||||
}
|
||||
|
||||
10% {
|
||||
transform: translateX(3px);
|
||||
background-color: var(--locked-color);
|
||||
}
|
||||
|
||||
15% {
|
||||
transform: translateX(-3px);
|
||||
}
|
||||
|
||||
25% {
|
||||
transform: translateX(3px);
|
||||
background-color: var(--locked-color);
|
||||
}
|
||||
|
||||
30%,
|
||||
70% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: var(--clr-bg-1);
|
||||
}
|
||||
}
|
87
ui/src/styles/markdown.css
Normal file
87
ui/src/styles/markdown.css
Normal file
@ -0,0 +1,87 @@
|
||||
.markdown {
|
||||
& p:last-child,
|
||||
& ul:last-child,
|
||||
& ol:last-child,
|
||||
& blockquote:last-child,
|
||||
& pre:last-child,
|
||||
& hr:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 2em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.5em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.17em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
h4 {
|
||||
font-size: 1em;
|
||||
margin-bottom: 0.8em;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-bottom: 1.2em;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: block;
|
||||
list-style-type: disc;
|
||||
margin: 1em 0;
|
||||
padding: 0 0 0 2em;
|
||||
}
|
||||
|
||||
ol {
|
||||
display: block;
|
||||
list-style-type: decimal;
|
||||
margin: 1em 0;
|
||||
padding: 0 0 0 2em;
|
||||
}
|
||||
|
||||
li {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 1em 0;
|
||||
padding: 0 0 0 2em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin: 1em 0;
|
||||
padding: 1em;
|
||||
background-color: var(--clr-scale-ntrl-90);
|
||||
border: 1px solid var(--clr-scale-ntrl-70);
|
||||
overflow: auto;
|
||||
border-radius: var(--radius-m);
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: monospace;
|
||||
background-color: var(--clr-scale-ntrl-90);
|
||||
border: 1px solid var(--clr-scale-ntrl-70);
|
||||
padding: 0 0.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
margin: 2em 0;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
}
|
254
ui/src/styles/reset.css
Normal file
254
ui/src/styles/reset.css
Normal file
@ -0,0 +1,254 @@
|
||||
/* BOILERPLATE CSS */
|
||||
/* reset all styles */
|
||||
@layer reset {
|
||||
/* base */
|
||||
*,
|
||||
:after,
|
||||
:before {
|
||||
box-sizing: border-box;
|
||||
border: 0 solid;
|
||||
}
|
||||
|
||||
html {
|
||||
overscroll-behavior: none;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
font-size: 13px;
|
||||
line-height: inherit;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow-y: hidden;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
line-height: inherit;
|
||||
|
||||
color: var(--clr-text-1);
|
||||
background-color: var(--clr-bg-2);
|
||||
|
||||
/* optimise font rendering */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
/* elements */
|
||||
hr {
|
||||
height: 0;
|
||||
color: inherit;
|
||||
border-top-width: 1px;
|
||||
}
|
||||
|
||||
abbr:where([title]) {
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-family:
|
||||
Söhne Mono,
|
||||
monospace;
|
||||
font-feature-settings: normal;
|
||||
font-variation-settings: normal;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
table {
|
||||
text-indent: 0;
|
||||
border-color: inherit;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
font-family: inherit;
|
||||
font-feature-settings: inherit;
|
||||
font-variation-settings: inherit;
|
||||
font-size: 100%;
|
||||
font-weight: inherit;
|
||||
line-height: inherit;
|
||||
letter-spacing: inherit;
|
||||
color: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
button,
|
||||
input:where([type='button']),
|
||||
input:where([type='reset']),
|
||||
input:where([type='submit']) {
|
||||
-webkit-appearance: button;
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
:-moz-focusring {
|
||||
outline: auto;
|
||||
}
|
||||
|
||||
:-moz-ui-invalid {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
::-webkit-inner-spin-button,
|
||||
::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type='search'] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
blockquote,
|
||||
dd,
|
||||
dl,
|
||||
figure,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hr,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
fieldset,
|
||||
legend {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
menu,
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dialog {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
input::-moz-placeholder,
|
||||
textarea::-moz-placeholder {
|
||||
opacity: 1;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
opacity: 1;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
[role='button'],
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
:disabled {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
audio,
|
||||
canvas,
|
||||
embed,
|
||||
iframe,
|
||||
img,
|
||||
object,
|
||||
svg,
|
||||
video {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
img,
|
||||
video {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
}
|
197
ui/src/styles/syntax-highlighting.css
Normal file
197
ui/src/styles/syntax-highlighting.css
Normal file
@ -0,0 +1,197 @@
|
||||
.token-variable {
|
||||
color: #8953800;
|
||||
}
|
||||
|
||||
.token-property {
|
||||
color: #0550ae;
|
||||
}
|
||||
|
||||
.token-type {
|
||||
color: #116329;
|
||||
}
|
||||
|
||||
.token-variable-special {
|
||||
color: #953800;
|
||||
}
|
||||
|
||||
.token-definition {
|
||||
color: #953800;
|
||||
}
|
||||
|
||||
/* .token-builtin {
|
||||
color: #d3869b;
|
||||
} */
|
||||
|
||||
.token-number {
|
||||
color: #0550ae;
|
||||
}
|
||||
|
||||
.token-string {
|
||||
color: #0550ae;
|
||||
}
|
||||
|
||||
.token-string-special {
|
||||
color: #0a3069;
|
||||
}
|
||||
|
||||
/* .token-atom {
|
||||
color: #0a3069;
|
||||
} */
|
||||
|
||||
.token-keyword {
|
||||
color: #cf222e;
|
||||
}
|
||||
|
||||
.token-comment {
|
||||
color: #6e7781;
|
||||
}
|
||||
|
||||
.token-meta {
|
||||
color: #1f2328;
|
||||
}
|
||||
|
||||
.token-invalid {
|
||||
color: #82071e;
|
||||
}
|
||||
|
||||
.token-tag {
|
||||
color: #116329;
|
||||
}
|
||||
|
||||
.token-attribute {
|
||||
color: #1f2328;
|
||||
}
|
||||
|
||||
.token-attribute-value {
|
||||
color: var(--color-token-attribute-value);
|
||||
}
|
||||
|
||||
.token-inserted {
|
||||
color: #116329;
|
||||
}
|
||||
|
||||
.token-deleted {
|
||||
color: #82071e;
|
||||
}
|
||||
|
||||
.token-heading {
|
||||
color: var(--color-token-variable-special);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token-link {
|
||||
color: var(--color-token-variable-special);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.token-strikethrough {
|
||||
text-decoration: strike-through;
|
||||
}
|
||||
|
||||
.token-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.dark {
|
||||
.token-variable {
|
||||
color: #79c0ff;
|
||||
}
|
||||
|
||||
.token-property {
|
||||
color: #79c0ff;
|
||||
}
|
||||
|
||||
.token-type {
|
||||
color: #7ee787;
|
||||
}
|
||||
|
||||
.token-variable-special {
|
||||
color: #79c0ff;
|
||||
}
|
||||
|
||||
.token-definition {
|
||||
color: #ffa657;
|
||||
}
|
||||
|
||||
/* .token-builtin {
|
||||
color: #d3869b;
|
||||
} */
|
||||
|
||||
.token-number {
|
||||
color: #a5d6ff;
|
||||
}
|
||||
|
||||
.token-string {
|
||||
color: #79c0ff;
|
||||
}
|
||||
|
||||
.token-string-special {
|
||||
color: #a5d6ff;
|
||||
}
|
||||
|
||||
/* .token-atom {
|
||||
color: #0a3069;
|
||||
} */
|
||||
|
||||
.token-keyword {
|
||||
color: #ff7b72;
|
||||
}
|
||||
|
||||
.token-comment {
|
||||
color: #8b949e;
|
||||
}
|
||||
|
||||
.token-meta {
|
||||
color: #ffa657;
|
||||
}
|
||||
|
||||
.token-invalid {
|
||||
color: #ffa198;
|
||||
}
|
||||
|
||||
.token-tag {
|
||||
color: #7ee787;
|
||||
}
|
||||
|
||||
.token-attribute {
|
||||
color: #e6edf3;
|
||||
}
|
||||
|
||||
.token-attribute-value {
|
||||
color: var(--color-token-attribute-value);
|
||||
}
|
||||
|
||||
.token-inserted {
|
||||
color: #7ee787;
|
||||
}
|
||||
|
||||
.token-deleted {
|
||||
color: #ffa198;
|
||||
}
|
||||
|
||||
.token-heading {
|
||||
color: var(--color-token-variable-special);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token-link {
|
||||
color: var(--color-token-variable-special);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.token-strikethrough {
|
||||
text-decoration: strike-through;
|
||||
}
|
||||
|
||||
.token-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
186
ui/src/styles/text-classes.css
Normal file
186
ui/src/styles/text-classes.css
Normal file
@ -0,0 +1,186 @@
|
||||
:root {
|
||||
--base-font-family: 'Inter', sans-serif;
|
||||
--serif-font-family: 'PP Editorial New', serif;
|
||||
--base-font-weight: 500;
|
||||
--semidold-font-weight: 600;
|
||||
--bold-font-weight: 700;
|
||||
--base-line-height: 120%;
|
||||
--text-body-line-height: 150%;
|
||||
}
|
||||
|
||||
/* text Base Classes */
|
||||
.text-base-8 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.5rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-base-9 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.563rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-base-10 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.625rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-base-11 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.6875rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-base-12 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.75rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-base-13 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-base-14 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.875rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-base-15 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.938rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-base-16 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 1rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-base-18 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 1.125rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
/* text Base Body Classes */
|
||||
.text-base-body-10 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.625rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
.text-base-body-11 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.6875rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
.text-base-body-12 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.75rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
.text-base-body-13 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.8125rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
.text-base-body-14 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.875rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
.text-base-body-15 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 0.938rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
.text-base-body-16 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 1rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
.text-base-body-18 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 1.125rem;
|
||||
font-weight: var(--base-font-weight);
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
/* text Head Classes */
|
||||
.text-head-20 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 1.25rem;
|
||||
font-weight: var(--bold-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-head-22 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 1.375rem;
|
||||
font-weight: var(--bold-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
.text-head-24 {
|
||||
font-family: var(--base-font-family);
|
||||
font-size: 1.5rem;
|
||||
font-weight: var(--bold-font-weight);
|
||||
line-height: var(--base-line-height);
|
||||
}
|
||||
|
||||
/* Serif Classes */
|
||||
.text-serif-40 {
|
||||
font-family: var(--serif-font-family);
|
||||
font-size: 2.5rem;
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
.text-serif-24 {
|
||||
font-family: var(--serif-font-family);
|
||||
font-size: 1.5rem;
|
||||
line-height: var(--text-body-line-height);
|
||||
}
|
||||
|
||||
/* modifiers */
|
||||
|
||||
.text-light {
|
||||
font-weight: var(--light-font-weight);
|
||||
}
|
||||
|
||||
.text-semibold {
|
||||
font-weight: var(--semidold-font-weight);
|
||||
}
|
||||
|
||||
.text-bold {
|
||||
font-weight: var(--bold-font-weight);
|
||||
}
|
32
ui/src/styles/text-input.css
Normal file
32
ui/src/styles/text-input.css
Normal file
@ -0,0 +1,32 @@
|
||||
.text-input {
|
||||
padding: 4px 10px;
|
||||
color: var(--clr-scale-ntrl-0);
|
||||
background-color: var(--clr-bg-1);
|
||||
border: 1px solid var(--clr-border-2);
|
||||
border-radius: var(--radius-s);
|
||||
transition: border-color var(--transition-fast);
|
||||
|
||||
&::placeholder {
|
||||
color: var(--clr-text-3);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--clr-border-1);
|
||||
}
|
||||
|
||||
&:focus,
|
||||
&:focus-within {
|
||||
border-color: var(--clr-border-1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:invalid {
|
||||
border-color: var(--clr-theme-err-element);
|
||||
}
|
||||
|
||||
&:disabled {
|
||||
color: var(--clr-scale-ntrl-60);
|
||||
border-color: var(--clr-scale-ntrl-70);
|
||||
background-color: var(--clr-bg-2);
|
||||
}
|
||||
}
|
368
ui/src/styles/tokens.css
Normal file
368
ui/src/styles/tokens.css
Normal file
@ -0,0 +1,368 @@
|
||||
/**
|
||||
* Design Tokens
|
||||
* Autogenerated from tokens.json.
|
||||
* DO NOT EDIT!
|
||||
*/
|
||||
|
||||
:root {
|
||||
--clr-bg-1: var(--clr-core-ntrl-100);
|
||||
--clr-bg-1-muted: var(--clr-core-ntrl-95);
|
||||
--clr-bg-2: var(--clr-core-ntrl-90);
|
||||
--clr-bg-2-muted: var(--clr-core-ntrl-80);
|
||||
--clr-bg-3: var(--clr-core-ntrl-80);
|
||||
--clr-bg-3-muted: var(--clr-core-ntrl-70);
|
||||
--clr-border-1: var(--clr-core-ntrl-60);
|
||||
--clr-border-2: var(--clr-core-ntrl-70);
|
||||
--clr-border-3: var(--clr-core-ntrl-80);
|
||||
--clr-commit-local: var(--clr-core-pop-50);
|
||||
--clr-commit-remote: var(--clr-core-ntrl-50);
|
||||
--clr-commit-shadow: var(--clr-core-ntrl-60);
|
||||
--clr-commit-upstream: var(--clr-core-warn-50);
|
||||
--clr-core-err-5: #260d0f;
|
||||
--clr-core-err-10: #4c1a1f;
|
||||
--clr-core-err-20: #6b242b;
|
||||
--clr-core-err-30: #95323c;
|
||||
--clr-core-err-40: #bd4852;
|
||||
--clr-core-err-50: #dc606b;
|
||||
--clr-core-err-60: #edb0ba;
|
||||
--clr-core-err-70: #f7d9dc;
|
||||
--clr-core-err-80: #fbe5e7;
|
||||
--clr-core-err-90: #fdf1f4;
|
||||
--clr-core-err-95: #fffafc;
|
||||
--clr-core-ntrl-0: #0a0a0a;
|
||||
--clr-core-ntrl-5: #181412;
|
||||
--clr-core-ntrl-10: #25211f;
|
||||
--clr-core-ntrl-20: #302c2b;
|
||||
--clr-core-ntrl-30: #4f4b48;
|
||||
--clr-core-ntrl-40: #67635e;
|
||||
--clr-core-ntrl-50: #867e79;
|
||||
--clr-core-ntrl-60: #b4afac;
|
||||
--clr-core-ntrl-70: #d4d0ce;
|
||||
--clr-core-ntrl-80: #eae9e8;
|
||||
--clr-core-ntrl-90: #f3f3f2;
|
||||
--clr-core-ntrl-95: #f7f7f6;
|
||||
--clr-core-ntrl-100: #ffffff;
|
||||
--clr-core-pop-5: #0e2a29;
|
||||
--clr-core-pop-10: #133937;
|
||||
--clr-core-pop-20: #1c5451;
|
||||
--clr-core-pop-30: #256f6b;
|
||||
--clr-core-pop-40: #2a928d;
|
||||
--clr-core-pop-50: #48a8a3;
|
||||
--clr-core-pop-60: #97cecb;
|
||||
--clr-core-pop-70: #c6e7e5;
|
||||
--clr-core-pop-80: #daf1f0;
|
||||
--clr-core-pop-90: #e9f7f6;
|
||||
--clr-core-pop-95: #f4fbfa;
|
||||
--clr-core-purp-5: #281d44;
|
||||
--clr-core-purp-10: #322352;
|
||||
--clr-core-purp-20: #4b3479;
|
||||
--clr-core-purp-30: #5e429a;
|
||||
--clr-core-purp-40: #7657b7;
|
||||
--clr-core-purp-50: #9978dd;
|
||||
--clr-core-purp-60: #c2acf1;
|
||||
--clr-core-purp-70: #dfd3f8;
|
||||
--clr-core-purp-80: #e9e0fa;
|
||||
--clr-core-purp-90: #f2edfc;
|
||||
--clr-core-purp-95: #f9f6fd;
|
||||
--clr-core-succ-5: #0d261a;
|
||||
--clr-core-succ-10: #18392a;
|
||||
--clr-core-succ-20: #22533c;
|
||||
--clr-core-succ-30: #2a7e57;
|
||||
--clr-core-succ-40: #469b73;
|
||||
--clr-core-succ-50: #4eb182;
|
||||
--clr-core-succ-60: #a1ceb9;
|
||||
--clr-core-succ-70: #cae8da;
|
||||
--clr-core-succ-80: #d6f0e4;
|
||||
--clr-core-succ-90: #e8f7f0;
|
||||
--clr-core-succ-95: #f6fcfb;
|
||||
--clr-core-warn-5: #261c0d;
|
||||
--clr-core-warn-10: #432d0f;
|
||||
--clr-core-warn-20: #604116;
|
||||
--clr-core-warn-30: #8a5914;
|
||||
--clr-core-warn-40: #c77f1a;
|
||||
--clr-core-warn-50: #dc9b14;
|
||||
--clr-core-warn-60: #f4bb6c;
|
||||
--clr-core-warn-70: #feddae;
|
||||
--clr-core-warn-80: #ffe8c7;
|
||||
--clr-core-warn-90: #fff2e0;
|
||||
--clr-core-warn-95: #fdf7ed;
|
||||
--clr-illustration-bg: #d6f0ee;
|
||||
--clr-illustration-fill: #fcfcf1;
|
||||
--clr-illustration-outline: #475050;
|
||||
--clr-overlay-bg: #d6d6d666;
|
||||
--clr-scale-err-5: var(--clr-core-err-5);
|
||||
--clr-scale-err-10: var(--clr-core-err-10);
|
||||
--clr-scale-err-20: var(--clr-core-err-20);
|
||||
--clr-scale-err-30: var(--clr-core-err-30);
|
||||
--clr-scale-err-40: var(--clr-core-err-40);
|
||||
--clr-scale-err-50: var(--clr-core-err-50);
|
||||
--clr-scale-err-60: var(--clr-core-err-60);
|
||||
--clr-scale-err-70: var(--clr-core-err-70);
|
||||
--clr-scale-err-80: var(--clr-core-err-80);
|
||||
--clr-scale-err-90: var(--clr-core-err-90);
|
||||
--clr-scale-err-95: var(--clr-core-err-95);
|
||||
--clr-scale-ntrl-0: var(--clr-core-ntrl-0);
|
||||
--clr-scale-ntrl-5: var(--clr-core-ntrl-5);
|
||||
--clr-scale-ntrl-10: var(--clr-core-ntrl-10);
|
||||
--clr-scale-ntrl-20: var(--clr-core-ntrl-20);
|
||||
--clr-scale-ntrl-30: var(--clr-core-ntrl-30);
|
||||
--clr-scale-ntrl-40: var(--clr-core-ntrl-40);
|
||||
--clr-scale-ntrl-50: var(--clr-core-ntrl-50);
|
||||
--clr-scale-ntrl-60: var(--clr-core-ntrl-60);
|
||||
--clr-scale-ntrl-70: var(--clr-core-ntrl-70);
|
||||
--clr-scale-ntrl-80: var(--clr-core-ntrl-80);
|
||||
--clr-scale-ntrl-90: var(--clr-core-ntrl-90);
|
||||
--clr-scale-ntrl-95: var(--clr-core-ntrl-95);
|
||||
--clr-scale-ntrl-100: var(--clr-core-ntrl-100);
|
||||
--clr-scale-pop-5: var(--clr-core-pop-5);
|
||||
--clr-scale-pop-10: var(--clr-core-pop-10);
|
||||
--clr-scale-pop-20: var(--clr-core-pop-20);
|
||||
--clr-scale-pop-30: var(--clr-core-pop-30);
|
||||
--clr-scale-pop-40: var(--clr-core-pop-40);
|
||||
--clr-scale-pop-50: var(--clr-core-pop-50);
|
||||
--clr-scale-pop-60: var(--clr-core-pop-60);
|
||||
--clr-scale-pop-70: var(--clr-core-pop-70);
|
||||
--clr-scale-pop-80: var(--clr-core-pop-80);
|
||||
--clr-scale-pop-90: var(--clr-core-pop-90);
|
||||
--clr-scale-pop-95: var(--clr-core-pop-95);
|
||||
--clr-scale-purp-5: var(--clr-core-purp-5);
|
||||
--clr-scale-purp-10: var(--clr-core-purp-10);
|
||||
--clr-scale-purp-20: var(--clr-core-purp-20);
|
||||
--clr-scale-purp-30: var(--clr-core-purp-30);
|
||||
--clr-scale-purp-40: var(--clr-core-purp-40);
|
||||
--clr-scale-purp-50: var(--clr-core-purp-50);
|
||||
--clr-scale-purp-60: var(--clr-core-purp-60);
|
||||
--clr-scale-purp-70: var(--clr-core-purp-70);
|
||||
--clr-scale-purp-80: var(--clr-core-purp-80);
|
||||
--clr-scale-purp-90: var(--clr-core-purp-90);
|
||||
--clr-scale-purp-95: var(--clr-core-purp-95);
|
||||
--clr-scale-succ-5: var(--clr-core-succ-5);
|
||||
--clr-scale-succ-10: var(--clr-core-succ-10);
|
||||
--clr-scale-succ-20: var(--clr-core-succ-20);
|
||||
--clr-scale-succ-30: var(--clr-core-succ-30);
|
||||
--clr-scale-succ-40: var(--clr-core-succ-40);
|
||||
--clr-scale-succ-50: var(--clr-core-succ-50);
|
||||
--clr-scale-succ-60: var(--clr-core-succ-60);
|
||||
--clr-scale-succ-70: var(--clr-core-succ-70);
|
||||
--clr-scale-succ-80: var(--clr-core-succ-80);
|
||||
--clr-scale-succ-90: var(--clr-core-succ-90);
|
||||
--clr-scale-succ-95: var(--clr-core-succ-95);
|
||||
--clr-scale-warn-5: var(--clr-core-warn-5);
|
||||
--clr-scale-warn-10: var(--clr-core-warn-10);
|
||||
--clr-scale-warn-20: var(--clr-core-warn-20);
|
||||
--clr-scale-warn-30: var(--clr-core-warn-30);
|
||||
--clr-scale-warn-40: var(--clr-core-warn-40);
|
||||
--clr-scale-warn-50: var(--clr-core-warn-50);
|
||||
--clr-scale-warn-60: var(--clr-core-warn-60);
|
||||
--clr-scale-warn-70: var(--clr-core-warn-70);
|
||||
--clr-scale-warn-80: var(--clr-core-warn-80);
|
||||
--clr-scale-warn-90: var(--clr-core-warn-90);
|
||||
--clr-scale-warn-95: var(--clr-core-warn-95);
|
||||
--clr-text-1: var(--clr-core-ntrl-5);
|
||||
--clr-text-2: var(--clr-core-ntrl-50);
|
||||
--clr-text-3: var(--clr-core-ntrl-60);
|
||||
--clr-theme-err-bg: var(--clr-core-err-90);
|
||||
--clr-theme-err-element: var(--clr-core-err-50);
|
||||
--clr-theme-err-element-hover: var(--clr-core-err-40);
|
||||
--clr-theme-err-on-element: var(--clr-core-err-95);
|
||||
--clr-theme-err-on-soft: var(--clr-core-err-20);
|
||||
--clr-theme-err-soft: var(--clr-core-err-80);
|
||||
--clr-theme-err-soft-hover: var(--clr-core-err-70);
|
||||
--clr-theme-ntrl-element: var(--clr-core-ntrl-30);
|
||||
--clr-theme-ntrl-element-hover: var(--clr-core-ntrl-20);
|
||||
--clr-theme-ntrl-on-element: var(--clr-core-ntrl-100);
|
||||
--clr-theme-ntrl-on-soft: var(--clr-scale-ntrl-0);
|
||||
--clr-theme-ntrl-soft: var(--clr-core-ntrl-90);
|
||||
--clr-theme-ntrl-soft-hover: var(--clr-core-ntrl-80);
|
||||
--clr-theme-pop-bg: var(--clr-core-pop-90);
|
||||
--clr-theme-pop-element: var(--clr-core-pop-50);
|
||||
--clr-theme-pop-element-hover: var(--clr-core-pop-40);
|
||||
--clr-theme-pop-on-element: var(--clr-core-ntrl-100);
|
||||
--clr-theme-pop-on-soft: var(--clr-core-pop-20);
|
||||
--clr-theme-pop-soft: var(--clr-core-pop-80);
|
||||
--clr-theme-pop-soft-hover: var(--clr-core-pop-70);
|
||||
--clr-theme-purp-bg: var(--clr-core-purp-90);
|
||||
--clr-theme-purp-element: var(--clr-core-purp-50);
|
||||
--clr-theme-purp-element-hover: var(--clr-core-purp-40);
|
||||
--clr-theme-purp-on-element: var(--clr-core-purp-95);
|
||||
--clr-theme-purp-on-soft: var(--clr-core-purp-20);
|
||||
--clr-theme-purp-soft: var(--clr-core-purp-80);
|
||||
--clr-theme-purp-soft-hover: var(--clr-core-purp-70);
|
||||
--clr-theme-succ-bg: var(--clr-core-succ-90);
|
||||
--clr-theme-succ-element: var(--clr-core-succ-50);
|
||||
--clr-theme-succ-element-hover: var(--clr-core-succ-40);
|
||||
--clr-theme-succ-on-element: var(--clr-core-succ-95);
|
||||
--clr-theme-succ-on-soft: var(--clr-core-succ-20);
|
||||
--clr-theme-succ-soft: var(--clr-core-succ-80);
|
||||
--clr-theme-succ-soft-hover: var(--clr-core-succ-70);
|
||||
--clr-theme-warn-bg: var(--clr-core-warn-90);
|
||||
--clr-theme-warn-element: var(--clr-core-warn-50);
|
||||
--clr-theme-warn-element-hover: var(--clr-core-warn-40);
|
||||
--clr-theme-warn-on-element: var(--clr-core-warn-95);
|
||||
--clr-theme-warn-on-soft: var(--clr-core-warn-20);
|
||||
--clr-theme-warn-soft: var(--clr-core-warn-80);
|
||||
--clr-theme-warn-soft-hover: var(--clr-core-warn-70);
|
||||
--fx-shadow-l: 0 10px 40px 0 #0000001a;
|
||||
--fx-shadow-m: 0 6px 30px 0 #00000014;
|
||||
--fx-shadow-s: 0 4px 14px 0 #0000000f;
|
||||
--radius-l: 0.75rem;
|
||||
--radius-m: 0.375rem;
|
||||
--radius-s: 0.25rem;
|
||||
--size-2: 0.125rem;
|
||||
--size-4: 0.25rem;
|
||||
--size-6: 0.375rem;
|
||||
--size-8: 0.5rem;
|
||||
--size-10: 0.625rem;
|
||||
--size-12: 0.75rem;
|
||||
--size-14: 0.875rem;
|
||||
--size-16: 1rem;
|
||||
--size-20: 1.25rem;
|
||||
--size-24: 1.5rem;
|
||||
--size-26: 1.625rem;
|
||||
--size-28: 1.75rem;
|
||||
--size-32: 2rem;
|
||||
--size-36: 2.25rem;
|
||||
--size-40: 2.5rem;
|
||||
--size-48: 3rem;
|
||||
--size-56: 3.5rem;
|
||||
--size-64: 4rem;
|
||||
--size-80: 5rem;
|
||||
--size-96: 6rem;
|
||||
--size-button: 1.75rem;
|
||||
--size-card-padding: 0.875rem;
|
||||
--size-cta: 2rem;
|
||||
--size-icon: 1rem;
|
||||
--size-tag: 1.375rem;
|
||||
}
|
||||
|
||||
:root.dark {
|
||||
--clr-bg-1: var(--clr-core-ntrl-10);
|
||||
--clr-bg-1-muted: var(--clr-core-ntrl-20);
|
||||
--clr-bg-2: var(--clr-core-ntrl-5);
|
||||
--clr-bg-2-muted: var(--clr-core-ntrl-10);
|
||||
--clr-bg-3: var(--clr-core-ntrl-0);
|
||||
--clr-bg-3-muted: var(--clr-core-ntrl-5);
|
||||
--clr-border-1: var(--clr-core-ntrl-40);
|
||||
--clr-border-2: var(--clr-core-ntrl-30);
|
||||
--clr-border-3: var(--clr-core-ntrl-20);
|
||||
--clr-commit-local: var(--clr-core-pop-50);
|
||||
--clr-commit-remote: var(--clr-core-ntrl-50);
|
||||
--clr-commit-shadow: var(--clr-core-ntrl-40);
|
||||
--clr-commit-upstream: var(--clr-core-warn-50);
|
||||
--clr-illustration-bg: #174543;
|
||||
--clr-illustration-fill: #95b5aa;
|
||||
--clr-illustration-outline: #142222;
|
||||
--clr-overlay-bg: #00000059;
|
||||
--clr-scale-err-5: var(--clr-core-err-95);
|
||||
--clr-scale-err-10: var(--clr-core-err-90);
|
||||
--clr-scale-err-20: var(--clr-core-err-80);
|
||||
--clr-scale-err-30: var(--clr-core-err-70);
|
||||
--clr-scale-err-40: var(--clr-core-err-60);
|
||||
--clr-scale-err-50: var(--clr-core-err-50);
|
||||
--clr-scale-err-60: var(--clr-core-err-40);
|
||||
--clr-scale-err-70: var(--clr-core-err-30);
|
||||
--clr-scale-err-80: var(--clr-core-err-20);
|
||||
--clr-scale-err-90: var(--clr-core-err-10);
|
||||
--clr-scale-err-95: var(--clr-core-err-5);
|
||||
--clr-scale-ntrl-0: var(--clr-core-ntrl-100);
|
||||
--clr-scale-ntrl-5: var(--clr-core-ntrl-95);
|
||||
--clr-scale-ntrl-10: var(--clr-core-ntrl-90);
|
||||
--clr-scale-ntrl-20: var(--clr-core-ntrl-80);
|
||||
--clr-scale-ntrl-30: var(--clr-core-ntrl-70);
|
||||
--clr-scale-ntrl-40: var(--clr-core-ntrl-60);
|
||||
--clr-scale-ntrl-50: var(--clr-core-ntrl-50);
|
||||
--clr-scale-ntrl-60: var(--clr-core-ntrl-40);
|
||||
--clr-scale-ntrl-70: var(--clr-core-ntrl-30);
|
||||
--clr-scale-ntrl-80: var(--clr-core-ntrl-20);
|
||||
--clr-scale-ntrl-90: var(--clr-core-ntrl-10);
|
||||
--clr-scale-ntrl-95: var(--clr-core-ntrl-5);
|
||||
--clr-scale-ntrl-100: var(--clr-core-ntrl-0);
|
||||
--clr-scale-pop-5: var(--clr-core-pop-95);
|
||||
--clr-scale-pop-10: var(--clr-core-pop-90);
|
||||
--clr-scale-pop-20: var(--clr-core-pop-80);
|
||||
--clr-scale-pop-30: var(--clr-core-pop-70);
|
||||
--clr-scale-pop-40: var(--clr-core-pop-60);
|
||||
--clr-scale-pop-50: var(--clr-core-pop-50);
|
||||
--clr-scale-pop-60: var(--clr-core-pop-40);
|
||||
--clr-scale-pop-70: var(--clr-core-pop-30);
|
||||
--clr-scale-pop-80: var(--clr-core-pop-20);
|
||||
--clr-scale-pop-90: var(--clr-core-pop-10);
|
||||
--clr-scale-pop-95: var(--clr-core-pop-5);
|
||||
--clr-scale-purp-5: var(--clr-core-purp-95);
|
||||
--clr-scale-purp-10: var(--clr-core-purp-90);
|
||||
--clr-scale-purp-20: var(--clr-core-purp-80);
|
||||
--clr-scale-purp-30: var(--clr-core-purp-70);
|
||||
--clr-scale-purp-40: var(--clr-core-purp-60);
|
||||
--clr-scale-purp-50: var(--clr-core-purp-50);
|
||||
--clr-scale-purp-60: var(--clr-core-purp-40);
|
||||
--clr-scale-purp-70: var(--clr-core-purp-30);
|
||||
--clr-scale-purp-80: var(--clr-core-purp-20);
|
||||
--clr-scale-purp-90: var(--clr-core-purp-10);
|
||||
--clr-scale-purp-95: var(--clr-core-purp-5);
|
||||
--clr-scale-succ-5: var(--clr-core-succ-95);
|
||||
--clr-scale-succ-10: var(--clr-core-succ-90);
|
||||
--clr-scale-succ-20: var(--clr-core-succ-80);
|
||||
--clr-scale-succ-30: var(--clr-core-succ-70);
|
||||
--clr-scale-succ-40: var(--clr-core-succ-60);
|
||||
--clr-scale-succ-50: var(--clr-core-succ-50);
|
||||
--clr-scale-succ-60: var(--clr-core-succ-40);
|
||||
--clr-scale-succ-70: var(--clr-core-succ-30);
|
||||
--clr-scale-succ-80: var(--clr-core-succ-20);
|
||||
--clr-scale-succ-90: var(--clr-core-succ-10);
|
||||
--clr-scale-succ-95: var(--clr-core-succ-5);
|
||||
--clr-scale-warn-5: var(--clr-core-warn-95);
|
||||
--clr-scale-warn-10: var(--clr-core-warn-90);
|
||||
--clr-scale-warn-20: var(--clr-core-warn-80);
|
||||
--clr-scale-warn-30: var(--clr-core-warn-70);
|
||||
--clr-scale-warn-40: var(--clr-core-warn-60);
|
||||
--clr-scale-warn-50: var(--clr-core-warn-50);
|
||||
--clr-scale-warn-60: var(--clr-core-warn-40);
|
||||
--clr-scale-warn-70: var(--clr-core-warn-30);
|
||||
--clr-scale-warn-80: var(--clr-core-warn-20);
|
||||
--clr-scale-warn-90: var(--clr-core-warn-10);
|
||||
--clr-scale-warn-95: var(--clr-core-warn-5);
|
||||
--clr-text-1: var(--clr-core-ntrl-95);
|
||||
--clr-text-2: var(--clr-core-ntrl-60);
|
||||
--clr-text-3: var(--clr-core-ntrl-40);
|
||||
--clr-theme-err-bg: var(--clr-core-err-10);
|
||||
--clr-theme-err-element: var(--clr-core-err-40);
|
||||
--clr-theme-err-element-hover: var(--clr-core-err-30);
|
||||
--clr-theme-err-on-element: var(--clr-core-err-95);
|
||||
--clr-theme-err-on-soft: var(--clr-core-err-90);
|
||||
--clr-theme-err-soft: var(--clr-core-err-20);
|
||||
--clr-theme-err-soft-hover: var(--clr-core-err-30);
|
||||
--clr-theme-ntrl-element: var(--clr-core-ntrl-80);
|
||||
--clr-theme-ntrl-element-hover: var(--clr-core-ntrl-95);
|
||||
--clr-theme-ntrl-on-element: var(--clr-core-ntrl-0);
|
||||
--clr-theme-ntrl-on-soft: var(--clr-core-ntrl-90);
|
||||
--clr-theme-ntrl-soft: var(--clr-core-ntrl-20);
|
||||
--clr-theme-ntrl-soft-hover: var(--clr-core-ntrl-30);
|
||||
--clr-theme-pop-bg: var(--clr-core-pop-10);
|
||||
--clr-theme-pop-element: var(--clr-core-pop-40);
|
||||
--clr-theme-pop-element-hover: var(--clr-core-pop-30);
|
||||
--clr-theme-pop-on-element: var(--clr-core-ntrl-100);
|
||||
--clr-theme-pop-on-soft: var(--clr-core-pop-90);
|
||||
--clr-theme-pop-soft: var(--clr-core-pop-10);
|
||||
--clr-theme-pop-soft-hover: var(--clr-core-pop-20);
|
||||
--clr-theme-purp-bg: var(--clr-core-purp-10);
|
||||
--clr-theme-purp-element: var(--clr-core-purp-40);
|
||||
--clr-theme-purp-element-hover: var(--clr-core-purp-30);
|
||||
--clr-theme-purp-on-element: var(--clr-core-purp-90);
|
||||
--clr-theme-purp-on-soft: var(--clr-core-purp-90);
|
||||
--clr-theme-purp-soft: var(--clr-core-purp-20);
|
||||
--clr-theme-purp-soft-hover: var(--clr-core-purp-10);
|
||||
--clr-theme-succ-bg: var(--clr-core-succ-10);
|
||||
--clr-theme-succ-element: var(--clr-core-succ-40);
|
||||
--clr-theme-succ-element-hover: var(--clr-core-succ-30);
|
||||
--clr-theme-succ-on-element: var(--clr-core-succ-90);
|
||||
--clr-theme-succ-on-soft: var(--clr-core-succ-90);
|
||||
--clr-theme-succ-soft: var(--clr-core-succ-10);
|
||||
--clr-theme-succ-soft-hover: var(--clr-core-succ-20);
|
||||
--clr-theme-warn-bg: var(--clr-core-warn-10);
|
||||
--clr-theme-warn-element: var(--clr-core-warn-40);
|
||||
--clr-theme-warn-element-hover: var(--clr-core-warn-30);
|
||||
--clr-theme-warn-on-element: var(--clr-core-warn-90);
|
||||
--clr-theme-warn-on-soft: var(--clr-core-warn-90);
|
||||
--clr-theme-warn-soft: var(--clr-core-warn-20);
|
||||
--clr-theme-warn-soft-hover: var(--clr-core-warn-30);
|
||||
}
|
33
ui/src/styles/tooltip.css
Normal file
33
ui/src/styles/tooltip.css
Normal file
@ -0,0 +1,33 @@
|
||||
.tooltip {
|
||||
pointer-events: none;
|
||||
background-color: var(--clr-core-ntrl-10);
|
||||
border-radius: var(--radius-s);
|
||||
border: 1px solid var(--clr-core-ntrl-30);
|
||||
color: var(--clr-core-ntrl-60);
|
||||
display: inline-block;
|
||||
padding: 6px 8px;
|
||||
z-index: var(--z-tooltip);
|
||||
|
||||
max-width: 180px;
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
top: -9999px;
|
||||
|
||||
opacity: 0;
|
||||
animation: showup-tooltip-animation 0.1s ease-out forwards;
|
||||
|
||||
&.no-max-width {
|
||||
max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes showup-tooltip-animation {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-0.2rem) scale(0.9);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
}
|
BIN
ui/static/favicon.png
Normal file
BIN
ui/static/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 949 B |
22
ui/svelte.config.js
Normal file
22
ui/svelte.config.js
Normal file
@ -0,0 +1,22 @@
|
||||
import staticAdapter from '@sveltejs/adapter-static';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
preprocess: vitePreprocess(),
|
||||
kit: {
|
||||
adapter: staticAdapter({
|
||||
pages: 'build',
|
||||
assets: 'build',
|
||||
fallback: 'index.html',
|
||||
precompress: true,
|
||||
strict: false
|
||||
})
|
||||
},
|
||||
compilerOptions: {
|
||||
css: 'injected',
|
||||
enableSourcemap: true
|
||||
}
|
||||
};
|
||||
|
||||
export default config;
|
19
ui/tsconfig.json
Normal file
19
ui/tsconfig.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./.svelte-kit/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"lib": ["dom", "dom.iterable", "ES2021"],
|
||||
"allowJs": true,
|
||||
"checkJs": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"sourceMap": true,
|
||||
"strict": true,
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"experimentalDecorators": true,
|
||||
"types": ["vitest/importMeta"]
|
||||
}
|
||||
}
|
38
ui/vite.config.ts
Normal file
38
ui/vite.config.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
sveltekit()
|
||||
],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
// prevent vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true
|
||||
},
|
||||
// to make use of `TAURI_DEBUG` and other env variables
|
||||
// https://tauri.studio/v1/api/config#buildconfig.beforedevcommand
|
||||
envPrefix: ['VITE_', 'TAURI_'],
|
||||
resolve: {
|
||||
conditions: ['es2015']
|
||||
},
|
||||
build: {
|
||||
// Tauri supports es2021
|
||||
target: process.env.TAURI_PLATFORM === 'windows' ? 'chrome105' : 'safari13',
|
||||
// minify production builds
|
||||
minify: !process.env.TAURI_DEBUG ? 'esbuild' : false,
|
||||
// ship sourcemaps for better sentry error reports
|
||||
sourcemap: true
|
||||
},
|
||||
test: {
|
||||
deps: {
|
||||
inline: ['sorcery']
|
||||
},
|
||||
includeSource: ['src/**/*.{js,ts}'],
|
||||
exclude: ['**/e2e/playwright/**/*', 'node_modules/**/*']
|
||||
}
|
||||
});
|
Loading…
Reference in New Issue
Block a user