diff --git a/.gitignore b/.gitignore
index 4916a65..6c37a5f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -114,3 +114,6 @@ public/
source/atom/love-*
source/vscode/themes/love-dark.color-theme.json
source/vscode/themes/love-light.color-theme.json
+source/sublime-text/love-dark.sublime-color-scheme
+source/sublime-text/love-light.sublime-color-scheme
+source/sublime-text/Love.sublime-package
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c7b8630..2a92490 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -23,6 +23,7 @@ pages:
script:
- yarn build
- cp 'public/index.html' 'public/404.html'
+ - cp 'source/sublime-text/Love.sublime-package' 'public/'
artifacts:
paths:
- public
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..41260d4
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "files.associations": {
+ "*.sublime-color-scheme": "jsonc"
+ }
+}
diff --git a/package.json b/package.json
index 088dbe4..4e5458f 100644
--- a/package.json
+++ b/package.json
@@ -6,10 +6,11 @@
"homepage": "https://love.holllo.cc",
"repository": "https://gitlab.com/holllo/love",
"scripts": {
- "build": "yarn build:atom && yarn build:images && yarn build:pages && yarn build:vscode",
+ "build": "yarn build:atom && yarn build:images && yarn build:pages && yarn build:sublime-text && yarn build:vscode",
"build:atom": "ts-node 'source/scripts/atom.ts'",
"build:images": "cpy 'source/pages/images/**' 'public/images/'",
"build:pages": "ts-node 'source/scripts/pages.ts'",
+ "build:sublime-text": "ts-node 'source/scripts/sublime-text.ts'",
"build:vscode": "ts-node 'source/scripts/vscode.ts'",
"watch:atom": "chokidar 'source/atom/*.less' 'source/atom/ui-template/*.less' -c 'yarn build:atom'",
"watch:vscode": "chokidar 'source/vscode/themes/love-template.color-theme.json' -c 'yarn build:vscode'",
@@ -19,6 +20,7 @@
"modern-normalize": "^0.6.0"
},
"devDependencies": {
+ "@types/jszip": "^3.1.7",
"@types/nunjucks": "^3.1.3",
"@types/sass": "^1.16.0",
"@types/tar": "^4.0.3",
@@ -28,6 +30,7 @@
"hsluv": "^0.1.0",
"hsluv-sass": "^1.0.0",
"htmlclean": "^3.0.8",
+ "jszip": "^3.3.0",
"mathsass": "^0.11.0",
"nunjucks": "^3.2.1",
"sass": "^1.26.3",
diff --git a/source/pages/index.html b/source/pages/index.html
index 9553464..060a764 100644
--- a/source/pages/index.html
+++ b/source/pages/index.html
@@ -167,7 +167,7 @@
Light
- Sublime Text*
+ Sublime Text
Visual Studio Code
diff --git a/source/pages/scss/style.scss b/source/pages/scss/style.scss
index a446b06..a260336 100644
--- a/source/pages/scss/style.scss
+++ b/source/pages/scss/style.scss
@@ -28,7 +28,8 @@ a:visited {
color: var(--dark-accent-4);
}
- &[href^='http']:not(.hide-external) {
+ &[href^='http']:not(.hide-external),
+ &.fake-external {
margin-right: 12px;
&::after {
diff --git a/source/scripts/sublime-text.ts b/source/scripts/sublime-text.ts
new file mode 100644
index 0000000..18ce934
--- /dev/null
+++ b/source/scripts/sublime-text.ts
@@ -0,0 +1,72 @@
+import {promises as fsp} from 'fs';
+import {join} from 'path';
+import Zip from 'jszip';
+import nunjucks from 'nunjucks';
+import {generateLove, LoveVariant} from './love';
+
+export async function entry(): Promise {
+ const themeDirectory: string = join(__dirname, '../sublime-text/');
+ // Configure Nunjucks to use the templates for `source/vscode/themes/`.
+ nunjucks.configure(themeDirectory, {
+ lstripBlocks: true,
+ trimBlocks: true,
+ throwOnUndefined: true
+ });
+
+ const love: LoveVariant[] = generateLove();
+ const sublimePackage: Zip = new Zip();
+ for (const variant of love) {
+ const template: string = await fsp.readFile(
+ join(themeDirectory, 'love-template.sublime-color-scheme'),
+ 'utf8'
+ );
+
+ const outputPath: string = join(
+ themeDirectory,
+ `love-${variant.name}.sublime-color-scheme`
+ );
+
+ const output: string = nunjucks.renderString(template, {
+ love: variant
+ });
+ let formattedOutput = '';
+ for (const line of output.split('\n')) {
+ // Don't include the line in the output if it's a comment:
+ // * Starts with `//` or
+ // * Starts with `/*` or
+ // * Ends with `*/`
+ // This allows us to use Nunjucks templating inside the JSON.
+ if (
+ /^\s+\/\/.+$/.exec(line) ||
+ /^\s+\/\*.+$/.exec(line) ||
+ /\*\/$/.exec(line)
+ ) {
+ continue;
+ }
+
+ formattedOutput += line;
+ formattedOutput += '\n';
+ }
+
+ let outputObject: any;
+ try {
+ outputObject = JSON.parse(formattedOutput);
+ } catch (error) {
+ console.log('Could not parse formatted output as regular JSON:');
+ throw error;
+ }
+
+ await fsp.writeFile(outputPath, JSON.stringify(outputObject));
+ sublimePackage.file(
+ `${String(outputObject.name)}.sublime-color-scheme`,
+ JSON.stringify(outputObject)
+ );
+ }
+
+ const zip: Buffer = await sublimePackage.generateAsync({type: 'nodebuffer'});
+ await fsp.writeFile(join(themeDirectory, 'Love.sublime-package'), zip);
+}
+
+if (require.main === module) {
+ entry();
+}
diff --git a/source/sublime-text/love-template.sublime-color-scheme b/source/sublime-text/love-template.sublime-color-scheme
new file mode 100644
index 0000000..88b754b
--- /dev/null
+++ b/source/sublime-text/love-template.sublime-color-scheme
@@ -0,0 +1,292 @@
+{
+ "name": "Love {{ love.name | capitalize }}",
+ "author": "Holllo",
+ "variables": {
+ "foreground1": "{{ love.colors.foreground1 }}",
+ "foreground2": "{{ love.colors.foreground2 }}",
+ "background1": "{{ love.colors.background1 }}",
+ "background2": "{{ love.colors.background2 }}",
+ /* {% for accent in love.colors.accents %}
+
+ "accent{{ loop.index }}": "{{ accent }}"
+ {{- ',' if loop.index !== love.colors.accents.length }}
+
+ {% endfor %} */
+ },
+ "globals": {
+ "foreground": "var(foreground1)",
+ "background": "var(background1)",
+ "caret": "color(var(foreground2) alpha(0.9))",
+ "block_caret": "color(var(foreground2) alpha(0.4))",
+ "invisibles": "color(var(foreground1) alpha(0.35))",
+ "line_highlight": "var(foreground1)",
+ "selection": "color(var(foreground1) alpha(0.2))",
+ "selection_border": "var(foreground1)",
+ "misspelling": "var(accent1)",
+ "active_guide": "color(var(accent2) alpha(0.69))",
+ "find_highlight_foreground": "var(background2)",
+ "find_highlight": "var(accent2)",
+ "brackets_options": "underline",
+ "brackets_foreground": "color(var(foreground1) alpha(0.65))",
+ "bracket_contents_options": "underline",
+ "bracket_contents_foreground": "color(var(foreground1) alpha(0.65))",
+ "tags_options": "stippled_underline"
+ },
+ "rules": [
+ {
+ "name": "Comment",
+ "scope": "comment",
+ "foreground": "color(var(foreground1) alpha(0.4))"
+ },
+ {
+ "name": "String",
+ "scope": "string",
+ "foreground": "var(accent4)"
+ },
+ {
+ "name": "Number",
+ "scope": "constant.numeric",
+ "foreground": "var(accent3)"
+ },
+ {
+ "name": "Built-in constant",
+ "scope": "constant.language",
+ "foreground": "var(accent3)"
+ },
+ {
+ "name": "User-defined constant",
+ "scope": "constant.character, constant.other",
+ "foreground": "var(accent3)"
+ },
+ {
+ "name": "Variable",
+ "scope": "variable"
+ },
+ {
+ "name": "Keyword",
+ "scope": "keyword - (source.c keyword.operator | source.c++ keyword.operator | source.objc keyword.operator | source.objc++ keyword.operator), keyword.operator.word",
+ "foreground": "var(accent9)"
+ },
+ {
+ "name": "Annotation Punctuation",
+ "scope": "punctuation.definition.annotation",
+ "foreground": "var(accent6)"
+ },
+ {
+ "name": "JavaScript Dollar",
+ "scope": "variable.other.dollar.only.js",
+ "foreground": "#f00"
+ },
+ {
+ "name": "Storage",
+ "scope": "storage",
+ "foreground": "var(accent9)"
+ },
+ {
+ "name": "Storage type",
+ "scope": "storage.type",
+ "foreground": "var(accent9)",
+ "font_style": "italic"
+ },
+ {
+ "name": "Entity name",
+ "scope": "entity.name - (entity.name.filename | entity.name.section | entity.name.tag | entity.name.label)",
+ "foreground": "var(accent8)"
+ },
+ {
+ "name": "Inherited class",
+ "scope": "entity.other.inherited-class",
+ "foreground": "var(accent4)",
+ "font_style": "italic underline"
+ },
+ {
+ "name": "Function argument",
+ "scope": "variable.parameter - (source.c | source.c++ | source.objc | source.objc++)",
+ "foreground": "var(accent3)",
+ "font_style": "italic"
+ },
+ {
+ "name": "Language variable",
+ "scope": "variable.language",
+ "foreground": "var(accent1)",
+ "font_style": "italic"
+ },
+ {
+ "name": "Tag name",
+ "scope": "entity.name.tag",
+ "foreground": "var(accent2)"
+ },
+ {
+ "name": "Tag attribute",
+ "scope": "entity.other.attribute-name",
+ "foreground": "var(accent3)"
+ },
+ {
+ "name": "Function call",
+ "scope": "variable.function, variable.annotation",
+ "foreground": "var(accent7)"
+ },
+ {
+ "name": "Library function",
+ "scope": "support.function, support.macro",
+ "foreground": "var(accent7)"
+ },
+ {
+ "name": "Library constant",
+ "scope": "support.constant",
+ "foreground": "var(accent3)"
+ },
+ {
+ "name": "Library class/type",
+ "scope": "support.type, support.class",
+ "foreground": "var(accent3)",
+ "font_style": "italic"
+ },
+ {
+ "name": "Library variable",
+ "scope": "support.other.variable"
+ },
+ {
+ "name": "Invalid",
+ "scope": "invalid",
+ "foreground": "var(accent1)"
+ },
+ {
+ "name": "Invalid deprecated",
+ "scope": "invalid.deprecated",
+ "foreground": "var(accent1)"
+ },
+ {
+ "name": "JSON String",
+ "scope": "meta.structure.dictionary.json string.quoted.double.json",
+ "foreground": "var(accent3)"
+ },
+ {
+ "name": "YAML String",
+ "scope": "string.unquoted.yaml",
+ "foreground": "var(foreground1)"
+ },
+ {
+ "name": "diff.header",
+ "scope": "meta.diff, meta.diff.header",
+ "foreground": "var(accent6)"
+ },
+ {
+ "name": "markup headings",
+ "scope": "markup.heading",
+ "font_style": "bold"
+ },
+ {
+ "name": "markup headings",
+ "scope": "markup.heading punctuation.definition.heading",
+ "foreground": "var(accent4)"
+ },
+ {
+ "name": "markup h1",
+ "scope": "markup.heading.1 punctuation.definition.heading",
+ "foreground": "var(accent4)"
+ },
+ {
+ "name": "markup links",
+ "scope": "markup.underline.link",
+ "foreground": "var(accent7)"
+ },
+ {
+ "name": "markup bold",
+ "scope": "markup.bold",
+ "font_style": "bold"
+ },
+ {
+ "name": "markup italic",
+ "scope": "markup.italic",
+ "font_style": "italic"
+ },
+ {
+ "name": "markup bold/italic",
+ "scope": "markup.italic markup.bold | markup.bold markup.italic",
+ "font_style": "bold italic"
+ },
+ {
+ "name": "markup hr",
+ "scope": "punctuation.definition.thematic-break",
+ "foreground": "var(foreground1)"
+ },
+ {
+ "name": "markup blockquote",
+ "scope": "markup.quote punctuation.definition.blockquote",
+ "foreground": "var(accent6)"
+ },
+ {
+ "name": "markup bullets",
+ "scope": "markup.list.numbered.bullet",
+ "foreground": "var(accent6)"
+ },
+ {
+ "name": "markup bullets",
+ "scope": "markup.list.unnumbered.bullet | (markup.list.numbered punctuation.definition)",
+ "foreground": "var(accent6)"
+ },
+ {
+ "name": "markup code",
+ "scope": "markup.raw",
+ "background": "color(var(foreground1) alpha(0.1))"
+ },
+ {
+ "name": "markup punctuation",
+ "scope": "markup.raw punctuation.definition.raw",
+ "foreground": "var(accent7)"
+ },
+ {
+ "name": "markup punctuation",
+ "scope": "text & (punctuation.definition.italic | punctuation.definition.bold | punctuation.definition.raw | punctuation.definition.link | punctuation.definition.metadata | punctuation.definition.image | punctuation.separator.table-cell | punctuation.section.table-header | punctuation.definition.constant)",
+ "foreground": "var(accent6)"
+ },
+ {
+ "name": "diff.deleted",
+ "scope": "markup.deleted",
+ "foreground": "var(accent1)"
+ },
+ {
+ "name": "diff.inserted",
+ "scope": "markup.inserted",
+ "foreground": "var(accent6)"
+ },
+ {
+ "name": "diff.changed",
+ "scope": "markup.changed",
+ "foreground": "var(accent3)"
+ },
+ {
+ "scope": "constant.numeric.line-number.find-in-files - match",
+ "foreground": "color(var(accent10) alpha(0.63))"
+ },
+ {
+ "scope": "entity.name.filename",
+ "foreground": "var(accent3)"
+ },
+ {
+ "scope": "message.error",
+ "foreground": "var(accent1)"
+ },
+ {
+ "scope": "diff.deleted",
+ "background": "var(accent1)",
+ "foreground": "var(background1)"
+ },
+ {
+ "scope": "diff.deleted.char",
+ "background": "var(accent1)",
+ "foreground": "var(background1)"
+ },
+ {
+ "scope": "diff.inserted",
+ "background": "var(accent4)",
+ "foreground": "var(background1)"
+ },
+ {
+ "scope": "diff.inserted.char",
+ "background": "var(accent4)",
+ "foreground": "var(background1)"
+ }
+ ]
+}
diff --git a/yarn.lock b/yarn.lock
index 1659cc1..502bae0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -201,6 +201,13 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==
+"@types/jszip@^3.1.7":
+ version "3.1.7"
+ resolved "https://registry.yarnpkg.com/@types/jszip/-/jszip-3.1.7.tgz#c45bd72b448b3fb002125282c57c36190247cb34"
+ integrity sha512-+XQKNI5zpxutK05hO67huUTw/2imXCuJWjnFdU63tRES/xXSX1yVR9cv/QAdO6Rii2y2tTHbzjQ4i2apLfuK0Q==
+ dependencies:
+ "@types/node" "*"
+
"@types/minimatch@*":
version "3.0.3"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
@@ -2365,6 +2372,11 @@ ignore@^5.0.5, ignore@^5.1.1, ignore@^5.1.4:
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf"
integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==
+immediate@~3.0.5:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
+ integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=
+
import-fresh@^3.0.0, import-fresh@^3.1.0:
version "3.2.1"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66"
@@ -2867,6 +2879,16 @@ jsonfile@^4.0.0:
optionalDependencies:
graceful-fs "^4.1.6"
+jszip@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.3.0.tgz#29d72c21a54990fa885b11fc843db320640d5271"
+ integrity sha512-EJ9k766htB1ZWnsV5ZMDkKLgA+201r/ouFF8R2OigVjVdcm2rurcBrrdXaeqBJbqnUVMko512PYmlncBKE1Huw==
+ dependencies:
+ lie "~3.3.0"
+ pako "~1.0.2"
+ readable-stream "~2.3.6"
+ set-immediate-shim "~1.0.1"
+
junk@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
@@ -2928,6 +2950,13 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
+lie@~3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a"
+ integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==
+ dependencies:
+ immediate "~3.0.5"
+
line-column-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/line-column-path/-/line-column-path-2.0.0.tgz#439aff48ef80d74c475801a25b560d021acf1288"
@@ -3671,7 +3700,7 @@ package-json@^6.3.0:
registry-url "^5.0.0"
semver "^6.2.0"
-pako@~1.0.5:
+pako@~1.0.2, pako@~1.0.5:
version "1.0.11"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf"
integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==
@@ -4137,7 +4166,7 @@ read-pkg@^5.2.0:
parse-json "^5.0.0"
type-fest "^0.6.0"
-readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6:
+readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@@ -4455,6 +4484,11 @@ set-blocking@^2.0.0:
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
+set-immediate-shim@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+ integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=
+
set-value@^2.0.0, set-value@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"