first-commit

This commit is contained in:
2025-08-25 15:46:12 +08:00
commit f4d95dfff4
5665 changed files with 705359 additions and 0 deletions

79
tools/generate-images.js Normal file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/env node
import imageminZopfli from 'imagemin-zopfli'; // eslint-disable-line import-x/no-unresolved
import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node'; // eslint-disable-line import-x/no-unresolved
import {optimize} from 'svgo';
import {readFile, writeFile} from 'node:fs/promises';
import {argv, exit} from 'node:process';
function doExit(err) {
if (err) console.error(err);
exit(err ? 1 : 0);
}
async function generate(svg, path, {size, bg}) {
const outputFile = new URL(path, import.meta.url);
if (String(outputFile).endsWith('.svg')) {
const {data} = optimize(svg, {
plugins: [
'preset-default',
'removeDimensions',
{
name: 'addAttributesToSVGElement',
params: {attributes: [{width: size}, {height: size}]},
},
],
});
await writeFile(outputFile, data);
return;
}
const {objects, options} = await loadSVGFromString(svg);
const canvas = new Canvas();
canvas.setDimensions({width: size, height: size});
const ctx = canvas.getContext('2d');
ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1);
if (bg) {
canvas.add(new Rect({
left: 0,
top: 0,
height: size * (1 / (size / options.height)),
width: size * (1 / (size / options.width)),
fill: 'white',
}));
}
canvas.add(util.groupSVGElements(objects, options));
canvas.renderAll();
let png = Buffer.from([]);
for await (const chunk of canvas.createPNGStream()) {
png = Buffer.concat([png, chunk]);
}
png = await imageminZopfli({more: true})(png);
await writeFile(outputFile, png);
}
async function main() {
const gitea = argv.slice(2).includes('gitea');
const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8');
const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8');
await Promise.all([
generate(logoSvg, '../public/assets/img/logo.svg', {size: 32}),
generate(logoSvg, '../public/assets/img/logo.png', {size: 512}),
generate(faviconSvg, '../public/assets/img/favicon.svg', {size: 32}),
generate(faviconSvg, '../public/assets/img/favicon.png', {size: 180}),
generate(logoSvg, '../public/assets/img/avatar_default.png', {size: 200}),
generate(logoSvg, '../public/assets/img/apple-touch-icon.png', {size: 180, bg: true}),
gitea && generate(logoSvg, '../public/assets/img/gitea.svg', {size: 32}),
]);
}
try {
doExit(await main());
} catch (err) {
doExit(err);
}

View File

@@ -0,0 +1,570 @@
{
"pkg:bat": {
"bat": [
".bat",
".cmd"
]
},
"pkg:clojure": {
"clojure": [
".clj",
".cljs",
".cljc",
".cljx",
".clojure",
".edn"
]
},
"pkg:coffeescript": {
"coffeescript": [
".coffee",
".cson",
".iced"
]
},
"pkg:configuration-editing": {
"jsonc": [
".code-workspace",
"language-configuration.json",
"icon-theme.json",
"color-theme.json"
],
"json": [
".code-profile"
]
},
"pkg:cpp": {
"c": [
".c",
".i"
],
"cpp": [
".cpp",
".cppm",
".cc",
".ccm",
".cxx",
".cxxm",
".c++",
".c++m",
".hpp",
".hh",
".hxx",
".h++",
".h",
".ii",
".ino",
".inl",
".ipp",
".ixx",
".tpp",
".txx",
".hpp.in",
".h.in"
],
"cuda-cpp": [
".cu",
".cuh"
]
},
"pkg:csharp": {
"csharp": [
".cs",
".csx",
".cake"
]
},
"pkg:css": {
"css": [
".css"
]
},
"pkg:dart": {
"dart": [
".dart"
]
},
"pkg:diff": {
"diff": [
".diff",
".patch",
".rej"
]
},
"pkg:docker": {
"dockerfile": [
".dockerfile",
".containerfile"
]
},
"pkg:fsharp": {
"fsharp": [
".fs",
".fsi",
".fsx",
".fsscript"
]
},
"pkg:git-base": {
"ignore": [
".gitignore_global",
".gitignore",
".git-blame-ignore-revs"
]
},
"pkg:go": {
"go": [
".go"
]
},
"pkg:groovy": {
"groovy": [
".groovy",
".gvy",
".gradle",
".jenkinsfile",
".nf"
]
},
"pkg:handlebars": {
"handlebars": [
".handlebars",
".hbs",
".hjs"
]
},
"pkg:hlsl": {
"hlsl": [
".hlsl",
".hlsli",
".fx",
".fxh",
".vsh",
".psh",
".cginc",
".compute"
]
},
"pkg:html": {
"html": [
".html",
".htm",
".shtml",
".xhtml",
".xht",
".mdoc",
".jsp",
".asp",
".aspx",
".jshtm",
".volt",
".ejs",
".rhtml"
]
},
"pkg:ini": {
"ini": [
".ini"
],
"properties": [
".conf",
".properties",
".cfg",
".directory",
".gitattributes",
".gitconfig",
".gitmodules",
".editorconfig",
".repo"
]
},
"pkg:java": {
"java": [
".java",
".jav"
]
},
"pkg:javascript": {
"javascriptreact": [
".jsx"
],
"javascript": [
".js",
".es6",
".mjs",
".cjs",
".pac"
]
},
"pkg:json": {
"json": [
".json",
".bowerrc",
".jscsrc",
".webmanifest",
".js.map",
".css.map",
".ts.map",
".har",
".jslintrc",
".jsonld",
".geojson",
".ipynb",
".vuerc"
],
"jsonc": [
".jsonc",
".eslintrc",
".eslintrc.json",
".jsfmtrc",
".jshintrc",
".swcrc",
".hintrc",
".babelrc"
],
"jsonl": [
".jsonl",
".ndjson"
],
"snippets": [
".code-snippets"
]
},
"pkg:julia": {
"julia": [
".jl"
],
"juliamarkdown": [
".jmd"
]
},
"pkg:latex": {
"tex": [
".sty",
".cls",
".bbx",
".cbx"
],
"latex": [
".tex",
".ltx",
".ctx"
],
"bibtex": [
".bib"
]
},
"pkg:less": {
"less": [
".less"
]
},
"pkg:log": {
"log": [
".log",
"*.log.?"
]
},
"pkg:lua": {
"lua": [
".lua"
]
},
"pkg:make": {
"makefile": [
".mak",
".mk"
]
},
"pkg:markdown-basics": {
"markdown": [
".md",
".mkd",
".mdwn",
".mdown",
".markdown",
".markdn",
".mdtxt",
".mdtext",
".workbook"
]
},
"pkg:ms-vscode.js-debug": {
"wat": [
".wat",
".wasm"
]
},
"pkg:npm": {
"ignore": [
".npmignore"
],
"properties": [
".npmrc"
]
},
"pkg:objective-c": {
"objective-c": [
".m"
],
"objective-cpp": [
".mm"
]
},
"pkg:perl": {
"perl": [
".pl",
".pm",
".pod",
".t",
".PL",
".psgi"
],
"raku": [
".raku",
".rakumod",
".rakutest",
".rakudoc",
".nqp",
".p6",
".pl6",
".pm6"
]
},
"pkg:php": {
"php": [
".php",
".php4",
".php5",
".phtml",
".ctp"
]
},
"pkg:powershell": {
"powershell": [
".ps1",
".psm1",
".psd1",
".pssc",
".psrc"
]
},
"pkg:pug": {
"jade": [
".pug",
".jade"
]
},
"pkg:python": {
"python": [
".py",
".rpy",
".pyw",
".cpy",
".gyp",
".gypi",
".pyi",
".ipy",
".pyt"
]
},
"pkg:r": {
"r": [
".r",
".rhistory",
".rprofile",
".rt"
]
},
"pkg:razor": {
"razor": [
".cshtml",
".razor"
]
},
"pkg:restructuredtext": {
"restructuredtext": [
".rst"
]
},
"pkg:ruby": {
"ruby": [
".rb",
".rbx",
".rjs",
".gemspec",
".rake",
".ru",
".erb",
".podspec",
".rbi"
]
},
"pkg:rust": {
"rust": [
".rs"
]
},
"pkg:scss": {
"scss": [
".scss"
]
},
"pkg:search-result": {
"search-result": [
".code-search"
]
},
"pkg:shaderlab": {
"shaderlab": [
".shader"
]
},
"pkg:shellscript": {
"shellscript": [
".sh",
".bash",
".bashrc",
".bash_aliases",
".bash_profile",
".bash_login",
".ebuild",
".eclass",
".profile",
".bash_logout",
".xprofile",
".xsession",
".xsessionrc",
".Xsession",
".zsh",
".zshrc",
".zprofile",
".zlogin",
".zlogout",
".zshenv",
".zsh-theme",
".fish",
".ksh",
".csh",
".cshrc",
".tcshrc",
".yashrc",
".yash_profile"
]
},
"pkg:sql": {
"sql": [
".sql",
".dsql"
]
},
"pkg:swift": {
"swift": [
".swift"
]
},
"pkg:typescript-basics": {
"typescript": [
".ts",
".cts",
".mts"
],
"typescriptreact": [
".tsx"
],
"json": [
".tsbuildinfo"
]
},
"pkg:vb": {
"vb": [
".vb",
".brs",
".vbs",
".bas",
".vba"
]
},
"pkg:xml": {
"xml": [
".xml",
".xsd",
".ascx",
".atom",
".axml",
".axaml",
".bpmn",
".cpt",
".csl",
".csproj",
".csproj.user",
".dita",
".ditamap",
".dtd",
".ent",
".mod",
".dtml",
".fsproj",
".fxml",
".iml",
".isml",
".jmx",
".launch",
".menu",
".mxml",
".nuspec",
".opml",
".owl",
".proj",
".props",
".pt",
".publishsettings",
".pubxml",
".pubxml.user",
".rbxlx",
".rbxmx",
".rdf",
".rng",
".rss",
".shproj",
".storyboard",
".svg",
".targets",
".tld",
".tmx",
".vbproj",
".vbproj.user",
".vcxproj",
".vcxproj.filters",
".wsdl",
".wxi",
".wxl",
".wxs",
".xaml",
".xbl",
".xib",
".xlf",
".xliff",
".xpdl",
".xul",
".xoml"
],
"xsl": [
".xsl",
".xslt"
]
},
"pkg:yaml": {
"yaml": [
".yaml",
".yml",
".eyaml",
".eyml",
".cff",
".yaml-tmlanguage",
".yaml-tmpreferences",
".yaml-tmtheme",
".winget"
]
}
}

112
tools/generate-svg.js Normal file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env node
import fastGlob from 'fast-glob';
import {optimize} from 'svgo';
import {parse} from 'node:path';
import {readFile, writeFile, mkdir} from 'node:fs/promises';
import {fileURLToPath} from 'node:url';
import {exit} from 'node:process';
import * as fs from 'node:fs';
const glob = (pattern) => fastGlob.sync(pattern, {
cwd: fileURLToPath(new URL('..', import.meta.url)),
absolute: true,
});
async function processAssetsSvgFile(file, {prefix, fullName} = {}) {
let name = fullName;
if (!name) {
name = parse(file).name;
if (prefix) name = `${prefix}-${name}`;
if (prefix === 'octicon') name = name.replace(/-[0-9]+$/, ''); // chop of '-16' on octicons
}
// Set the `xmlns` attribute so that the files are displayable in standalone documents
// The svg backend module will strip the attribute during startup for inline display
const {data} = optimize(await readFile(file, 'utf8'), {
plugins: [
{name: 'preset-default'},
{name: 'removeDimensions'},
{name: 'removeTitle'},
{name: 'prefixIds', params: {prefix: () => name}},
{name: 'addClassesToSVGElement', params: {classNames: ['svg', name]}},
{
name: 'addAttributesToSVGElement', params: {
attributes: [
{'xmlns': 'http://www.w3.org/2000/svg'},
{'width': '16'}, {'height': '16'}, {'aria-hidden': 'true'},
],
},
},
],
});
await writeFile(fileURLToPath(new URL(`../public/assets/img/svg/${name}.svg`, import.meta.url)), data);
}
function processAssetsSvgFiles(pattern, opts) {
return glob(pattern).map((file) => processAssetsSvgFile(file, opts));
}
async function processMaterialFileIcons() {
const files = glob('node_modules/material-icon-theme/icons/*.svg');
const svgSymbols = {};
for (const file of files) {
// remove all unnecessary attributes, only keep "viewBox"
const {data} = optimize(await readFile(file, 'utf8'), {
plugins: [
{name: 'preset-default'},
{name: 'removeDimensions'},
{name: 'removeXMLNS'},
{name: 'removeAttrs', params: {attrs: 'xml:space', elemSeparator: ','}},
],
});
const svgName = parse(file).name;
// intentionally use single quote here to avoid escaping
svgSymbols[svgName] = data.replace(/"/g, `'`);
}
fs.writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-svgs.json`, import.meta.url)), JSON.stringify(svgSymbols, null, 2));
const vscodeExtensionsJson = await readFile(fileURLToPath(new URL(`generate-svg-vscode-extensions.json`, import.meta.url)));
const vscodeExtensions = JSON.parse(vscodeExtensionsJson);
const iconRulesJson = await readFile(fileURLToPath(new URL(`../node_modules/material-icon-theme/dist/material-icons.json`, import.meta.url)));
const iconRules = JSON.parse(iconRulesJson);
// The rules are from VSCode material-icon-theme, we need to adjust them to our needs
// 1. We only use lowercase filenames to match (it should be good enough for most cases and more efficient)
// 2. We do not have a "Language ID" system:
// * https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers
// * https://github.com/microsoft/vscode/tree/1.98.0/extensions
delete iconRules.iconDefinitions;
for (const [k, v] of Object.entries(iconRules.fileNames)) iconRules.fileNames[k.toLowerCase()] = v;
for (const [k, v] of Object.entries(iconRules.folderNames)) iconRules.folderNames[k.toLowerCase()] = v;
for (const [k, v] of Object.entries(iconRules.fileExtensions)) iconRules.fileExtensions[k.toLowerCase()] = v;
// Use VSCode's "Language ID" mapping from its extensions
for (const [_, langIdExtMap] of Object.entries(vscodeExtensions)) {
for (const [langId, names] of Object.entries(langIdExtMap)) {
for (const name of names) {
const nameLower = name.toLowerCase();
if (nameLower[0] === '.') {
iconRules.fileExtensions[nameLower.substring(1)] ??= langId;
} else {
iconRules.fileNames[nameLower] ??= langId;
}
}
}
}
const iconRulesPretty = JSON.stringify(iconRules, null, 2);
fs.writeFileSync(fileURLToPath(new URL(`../options/fileicon/material-icon-rules.json`, import.meta.url)), iconRulesPretty);
}
async function main() {
await mkdir(fileURLToPath(new URL('../public/assets/img/svg', import.meta.url)), {recursive: true});
await Promise.all([
...processAssetsSvgFiles('node_modules/@primer/octicons/build/svg/*-16.svg', {prefix: 'octicon'}),
...processAssetsSvgFiles('web_src/svg/*.svg'),
...processAssetsSvgFiles('public/assets/img/gitea.svg', {fullName: 'gitea-gitea'}),
processMaterialFileIcons(),
]);
}
try {
await main();
} catch (err) {
console.error(err);
exit(1);
}

23
tools/lint-go-gopls.sh Normal file
View File

@@ -0,0 +1,23 @@
#!/bin/bash
set -uo pipefail
cd "$(dirname -- "${BASH_SOURCE[0]}")" && cd ..
IGNORE_PATTERNS=(
"is deprecated" # TODO: fix these
)
# lint all go files with 'gopls check' and look for lines starting with the
# current absolute path, indicating a error was found. This is necessary
# because the tool does not set non-zero exit code when errors are found.
# ref: https://github.com/golang/go/issues/67078
ERROR_LINES=$("$GO" run "$GOPLS_PACKAGE" check -severity=warning "$@" 2>/dev/null | grep -E "^$PWD" | grep -vFf <(printf '%s\n' "${IGNORE_PATTERNS[@]}"));
NUM_ERRORS=$(echo -n "$ERROR_LINES" | wc -l)
if [ "$NUM_ERRORS" -eq "0" ]; then
exit 0;
else
echo "$ERROR_LINES"
echo "Found $NUM_ERRORS 'gopls check' errors"
exit 1;
fi

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env node
import {readdirSync, readFileSync} from 'node:fs';
import {parse, relative} from 'node:path';
import {fileURLToPath} from 'node:url';
import {exit} from 'node:process';
import fastGlob from 'fast-glob';
const knownSvgs = new Set();
for (const file of readdirSync(new URL('../public/assets/img/svg', import.meta.url))) {
knownSvgs.add(parse(file).name);
}
const rootPath = fileURLToPath(new URL('..', import.meta.url));
let hadErrors = false;
for (const file of fastGlob.sync(fileURLToPath(new URL('../templates/**/*.tmpl', import.meta.url)))) {
const content = readFileSync(file, 'utf8');
for (const [_, name] of content.matchAll(/svg ["'`]([^"'`]+)["'`]/g)) {
if (!knownSvgs.has(name)) {
console.info(`SVG "${name}" not found, used in ${relative(rootPath, file)}`);
hadErrors = true;
}
}
}
exit(hadErrors ? 1 : 0);

21
tools/misspellings.csv Normal file
View File

@@ -0,0 +1,21 @@
acounts,accounts
canidate,candidate
comfirm,confirm
converage,coverage
currrently,currently
delimeter,delimiter
differrent,different
exclusing,excluding
finshed,finished
formated,formatted
inderect,indirect
insuficient,insufficient
likly,likely
mergable,mergeable
overrided,overridden
priortized,prioritized
registeration,registration
reuqest,request
reviwer,reviewer
superceded,superseded
underlaying,underlying
1 acounts accounts
2 canidate candidate
3 comfirm confirm
4 converage coverage
5 currrently currently
6 delimeter delimiter
7 differrent different
8 exclusing excluding
9 finshed finished
10 formated formatted
11 inderect indirect
12 insuficient insufficient
13 likly likely
14 mergable mergeable
15 overrided overridden
16 priortized prioritized
17 registeration registration
18 reuqest request
19 reviwer reviewer
20 superceded superseded
21 underlaying underlying

8
tools/watch.sh Normal file
View File

@@ -0,0 +1,8 @@
#!/bin/bash
set -euo pipefail
make --no-print-directory watch-frontend &
make --no-print-directory watch-backend &
trap 'kill $(jobs -p)' EXIT
wait