Compare commits
No commits in common. "main" and "legacy" have entirely different histories.
|
@ -1,6 +0,0 @@
|
|||
.husky
|
||||
.vscode
|
||||
node_modules
|
||||
public
|
||||
dist
|
||||
.yarn
|
23
.eslintrc.js
|
@ -1,23 +0,0 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
node: true,
|
||||
es2022: true,
|
||||
browser: true,
|
||||
},
|
||||
extends: ["eslint:recommended", "plugin:astro/recommended"],
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ["*.astro"],
|
||||
parser: "astro-eslint-parser",
|
||||
parserOptions: {
|
||||
parser: "@typescript-eslint/parser",
|
||||
extraFileExtensions: [".astro"],
|
||||
},
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
};
|
42
.gitignore
vendored
|
@ -1,41 +1 @@
|
|||
# build output
|
||||
dist/
|
||||
.output/
|
||||
dist.zip
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# ignore .astro directory
|
||||
.astro
|
||||
|
||||
# ignore Jampack cache files
|
||||
.jampack/
|
||||
|
||||
# yarn
|
||||
.yarn/*
|
||||
!.yarn/patches
|
||||
!.yarn/plugins
|
||||
!.yarn/releases
|
||||
!.yarn/sdks
|
||||
!.yarn/versions
|
||||
.pnp.*
|
||||
|
||||
# blog, meta
|
||||
src/content/blog/*
|
||||
Matomo.astro
|
||||
src/pages/pages/*
|
||||
project.geany
|
||||
|
|
12
.gitmodules
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
[submodule "deps/bulma"]
|
||||
path = deps/bulma
|
||||
url = https://github.com/jgthms/bulma
|
||||
[submodule "deps/forkawesome"]
|
||||
path = deps/forkawesome
|
||||
url = https://github.com/ForkAwesome/Fork-Awesome
|
||||
[submodule "deps/rssparser"]
|
||||
path = deps/rssparser
|
||||
url = https://github.com/rbren/rss-parser
|
||||
[submodule "deps/jquery"]
|
||||
path = deps/jquery
|
||||
url = https://github.com/jquery/jquery
|
|
@ -1,4 +0,0 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
5
.idea/.gitignore
generated
vendored
|
@ -1,5 +0,0 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
125
.idea/.workspace.xml.~1614e1ad
generated
|
@ -1,125 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="001707ae-acae-4d7f-8b81-ae30629b8118" name="Changes" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="CSS File" />
|
||||
<option value="JavaScript File" />
|
||||
<option value="Astro Component" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 8
|
||||
}</component>
|
||||
<component name="ProjectId" id="2gyJYMuj2FYRcYSjxshnxqqMAxs" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"JavaScript Debug.localhost:4321.executor": "Run",
|
||||
"Node.js.Unnamed.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"last_opened_file_path": "/home/massive/Dev/website/src/pages/contact",
|
||||
"list.type.of.created.stylesheet": "CSS",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"npm.build.executor": "Run",
|
||||
"npm.dev --host.executor": "Run",
|
||||
"npm.dev.executor": "Run",
|
||||
"settings.editor.selected.configurable": "watcher.settings",
|
||||
"ts.external.directory.path": "/home/massive/Dev/website/node_modules/typescript/lib",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/src/pages/contact" />
|
||||
<recent name="$PROJECT_DIR$/public" />
|
||||
<recent name="$PROJECT_DIR$/src/content/pages" />
|
||||
<recent name="$PROJECT_DIR$/src/pages" />
|
||||
<recent name="$PROJECT_DIR$/public/assets" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="npm.dev">
|
||||
<configuration name="build" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="build" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="dev" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="dev" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<list>
|
||||
<item itemvalue="npm.build" />
|
||||
<item itemvalue="npm.dev" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-1d06a55b98c1-0b3e54e931b4-JavaScript-WS-241.17011.90" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="001707ae-acae-4d7f-8b81-ae30629b8118" name="Changes" comment="" />
|
||||
<created>1716663930272</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1716663930272</updated>
|
||||
<workItem from="1716663931436" duration="8820000" />
|
||||
<workItem from="1716711982151" duration="4548000" />
|
||||
<workItem from="1716722994034" duration="1057000" />
|
||||
<workItem from="1716752557400" duration="3447000" />
|
||||
<workItem from="1716834371444" duration="12421000" />
|
||||
<workItem from="1716881546273" duration="6394000" />
|
||||
<workItem from="1717097685944" duration="5772000" />
|
||||
<workItem from="1717183825428" duration="5319000" />
|
||||
<workItem from="1717700345223" duration="5114000" />
|
||||
<workItem from="1718037090569" duration="12037000" />
|
||||
<workItem from="1718132719188" duration="6719000" />
|
||||
<workItem from="1718313002958" duration="2163000" />
|
||||
<workItem from="1720095094533" duration="5678000" />
|
||||
<workItem from="1720103228993" duration="231000" />
|
||||
<workItem from="1720103468928" duration="6739000" />
|
||||
<workItem from="1720210527152" duration="8062000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
125
.idea/.workspace.xml.~3f6cc896
generated
|
@ -1,125 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="001707ae-acae-4d7f-8b81-ae30629b8118" name="Changes" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="CSS File" />
|
||||
<option value="JavaScript File" />
|
||||
<option value="Astro Component" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 8
|
||||
}</component>
|
||||
<component name="ProjectId" id="2gyJYMuj2FYRcYSjxshnxqqMAxs" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"JavaScript Debug.localhost:4321.executor": "Run",
|
||||
"Node.js.Unnamed.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"last_opened_file_path": "/home/massive/Dev/website/src/pages/contact",
|
||||
"list.type.of.created.stylesheet": "CSS",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"npm.build.executor": "Run",
|
||||
"npm.dev --host.executor": "Run",
|
||||
"npm.dev.executor": "Run",
|
||||
"settings.editor.selected.configurable": "watcher.settings",
|
||||
"ts.external.directory.path": "/home/massive/Dev/website/node_modules/typescript/lib",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/src/pages/contact" />
|
||||
<recent name="$PROJECT_DIR$/public" />
|
||||
<recent name="$PROJECT_DIR$/src/content/pages" />
|
||||
<recent name="$PROJECT_DIR$/src/pages" />
|
||||
<recent name="$PROJECT_DIR$/public/assets" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="npm.dev">
|
||||
<configuration name="build" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="build" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="dev" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="dev" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<list>
|
||||
<item itemvalue="npm.build" />
|
||||
<item itemvalue="npm.dev" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-1d06a55b98c1-0b3e54e931b4-JavaScript-WS-241.17011.90" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="001707ae-acae-4d7f-8b81-ae30629b8118" name="Changes" comment="" />
|
||||
<created>1716663930272</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1716663930272</updated>
|
||||
<workItem from="1716663931436" duration="8820000" />
|
||||
<workItem from="1716711982151" duration="4548000" />
|
||||
<workItem from="1716722994034" duration="1057000" />
|
||||
<workItem from="1716752557400" duration="3447000" />
|
||||
<workItem from="1716834371444" duration="12421000" />
|
||||
<workItem from="1716881546273" duration="6394000" />
|
||||
<workItem from="1717097685944" duration="5772000" />
|
||||
<workItem from="1717183825428" duration="5319000" />
|
||||
<workItem from="1717700345223" duration="5114000" />
|
||||
<workItem from="1718037090569" duration="12037000" />
|
||||
<workItem from="1718132719188" duration="6719000" />
|
||||
<workItem from="1718313002958" duration="2163000" />
|
||||
<workItem from="1720095094533" duration="5678000" />
|
||||
<workItem from="1720103228993" duration="231000" />
|
||||
<workItem from="1720103468928" duration="6739000" />
|
||||
<workItem from="1720210527152" duration="8062000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
58
.idea/codeStyles/Project.xml
generated
|
@ -1,58 +0,0 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<option name="LINE_SEPARATOR" value=" " />
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="80" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
|
@ -1,5 +0,0 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
23
.idea/inspectionProfiles/Project_Default.xml
generated
|
@ -1,23 +0,0 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="myValues">
|
||||
<value>
|
||||
<list size="8">
|
||||
<item index="0" class="java.lang.String" itemvalue="nobr" />
|
||||
<item index="1" class="java.lang.String" itemvalue="noembed" />
|
||||
<item index="2" class="java.lang.String" itemvalue="comment" />
|
||||
<item index="3" class="java.lang.String" itemvalue="noscript" />
|
||||
<item index="4" class="java.lang.String" itemvalue="embed" />
|
||||
<item index="5" class="java.lang.String" itemvalue="script" />
|
||||
<item index="6" class="java.lang.String" itemvalue="Navbar.Burger" />
|
||||
<item index="7" class="java.lang.String" itemvalue="Navbar.Menu" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myCustomValuesEnabled" value="true" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
6
.idea/jsLibraryMappings.xml
generated
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="JavaScriptLibraryMappings">
|
||||
<includedPredefinedLibrary name="Node.js Core" />
|
||||
</component>
|
||||
</project>
|
8
.idea/modules.xml
generated
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/website.iml" filepath="$PROJECT_DIR$/.idea/website.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
generated
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
4
.idea/watcherTasks.xml
generated
|
@ -1,4 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectTasksOptions" suppressed-tasks="Sass;SCSS" />
|
||||
</project>
|
12
.idea/website.iml
generated
|
@ -1,12 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -1,140 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="SELECTIVE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="001707ae-acae-4d7f-8b81-ae30629b8118" name="Changes" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="CSS File" />
|
||||
<option value="JavaScript File" />
|
||||
<option value="Astro Component" />
|
||||
<option value="TypeScript File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 8
|
||||
}</component>
|
||||
<component name="ProjectId" id="2gyJYMuj2FYRcYSjxshnxqqMAxs" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"JavaScript Debug.localhost:4321.executor": "Run",
|
||||
"Node.js.Unnamed.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"last_opened_file_path": "/home/massive/Dev/website/src/content/blog",
|
||||
"list.type.of.created.stylesheet": "CSS",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.detected.package.tslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"node.js.selected.package.tslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"npm.build.executor": "Run",
|
||||
"npm.dev --host.executor": "Run",
|
||||
"npm.dev.executor": "Run",
|
||||
"settings.editor.selected.configurable": "watcher.settings",
|
||||
"ts.external.directory.path": "/home/massive/Dev/website/node_modules/typescript/lib",
|
||||
"vue.rearranger.settings.migration": "true"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/src/content/blog" />
|
||||
<recent name="$PROJECT_DIR$/src/pages" />
|
||||
<recent name="$PROJECT_DIR$/src/pages/contact" />
|
||||
<recent name="$PROJECT_DIR$/public" />
|
||||
<recent name="$PROJECT_DIR$/src/content/pages" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/public/assets" />
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="npm.build">
|
||||
<configuration name="build" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="build" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="dev" type="js.build_tools.npm" nameIsGenerated="true">
|
||||
<package-json value="$PROJECT_DIR$/package.json" />
|
||||
<command value="run" />
|
||||
<scripts>
|
||||
<script value="dev" />
|
||||
</scripts>
|
||||
<node-interpreter value="project" />
|
||||
<envs />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<list>
|
||||
<item itemvalue="npm.build" />
|
||||
<item itemvalue="npm.dev" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="SharedIndexes">
|
||||
<attachedChunks>
|
||||
<set>
|
||||
<option value="bundled-js-predefined-1d06a55b98c1-0b3e54e931b4-JavaScript-WS-241.17011.90" />
|
||||
</set>
|
||||
</attachedChunks>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="001707ae-acae-4d7f-8b81-ae30629b8118" name="Changes" comment="" />
|
||||
<created>1716663930272</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1716663930272</updated>
|
||||
<workItem from="1716663931436" duration="8820000" />
|
||||
<workItem from="1716711982151" duration="4548000" />
|
||||
<workItem from="1716722994034" duration="1057000" />
|
||||
<workItem from="1716752557400" duration="3447000" />
|
||||
<workItem from="1716834371444" duration="12421000" />
|
||||
<workItem from="1716881546273" duration="6394000" />
|
||||
<workItem from="1717097685944" duration="5772000" />
|
||||
<workItem from="1717183825428" duration="5319000" />
|
||||
<workItem from="1717700345223" duration="5114000" />
|
||||
<workItem from="1718037090569" duration="12037000" />
|
||||
<workItem from="1718132719188" duration="6719000" />
|
||||
<workItem from="1718313002958" duration="2163000" />
|
||||
<workItem from="1720095094533" duration="5678000" />
|
||||
<workItem from="1720103228993" duration="231000" />
|
||||
<workItem from="1720103468928" duration="6739000" />
|
||||
<workItem from="1720210527152" duration="8062000" />
|
||||
<workItem from="1720810701145" duration="6508000" />
|
||||
<workItem from="1721114353574" duration="9589000" />
|
||||
<workItem from="1721201074971" duration="13333000" />
|
||||
<workItem from="1721682868864" duration="3726000" />
|
||||
<workItem from="1721728352517" duration="17607000" />
|
||||
<workItem from="1721808118785" duration="52668000" />
|
||||
<workItem from="1722537806385" duration="1080000" />
|
||||
<workItem from="1722591971234" duration="8060000" />
|
||||
<workItem from="1722926793892" duration="1858000" />
|
||||
<workItem from="1722930171277" duration="3925000" />
|
||||
<workItem from="1723123796550" duration="1901000" />
|
||||
<workItem from="1723125710139" duration="7928000" />
|
||||
<workItem from="1723285063430" duration="5988000" />
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
</project>
|
|
@ -1,4 +0,0 @@
|
|||
{
|
||||
"MD033": false,
|
||||
"MD013": false
|
||||
}
|
2
.npmrc
|
@ -1,2 +0,0 @@
|
|||
# Expose Astro dependencies for `pnpm` users
|
||||
shamefully-hoist=true
|
|
@ -1,13 +0,0 @@
|
|||
# Ignore everything
|
||||
/*
|
||||
|
||||
# Except these files & folders
|
||||
!/src
|
||||
!/public
|
||||
!/.github
|
||||
!tsconfig.json
|
||||
!astro.config.ts
|
||||
!package.json
|
||||
!.prettierrc
|
||||
!.eslintrc.js
|
||||
!README.md
|
20
.prettierrc
|
@ -1,20 +0,0 @@
|
|||
{
|
||||
"arrowParens": "avoid",
|
||||
"semi": true,
|
||||
"tabWidth": 2,
|
||||
"printWidth": 80,
|
||||
"singleQuote": false,
|
||||
"jsxSingleQuote": false,
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": true,
|
||||
"endOfLine": "lf",
|
||||
"plugins": ["prettier-plugin-astro", "prettier-plugin-tailwindcss"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": "*.astro",
|
||||
"options": {
|
||||
"parser": "astro"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
167
LICENSE
|
@ -1,31 +1,5 @@
|
|||
==== Original license for the AstroPaper Theme Code ====
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Sat Naing
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
===== License for all the changes implemented by MassiveBox =====
|
||||
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
|
@ -33,15 +7,17 @@ SOFTWARE.
|
|||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
|
@ -50,34 +26,44 @@ them if you wish), that you receive source code or can get it if you
|
|||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
@ -86,7 +72,7 @@ modification follow.
|
|||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
@ -563,45 +549,35 @@ to collect a royalty for further conveying from those to whom you convey
|
|||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
|
@ -659,29 +635,40 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
|
|
99
README.md
|
@ -1,94 +1,11 @@
|
|||
# MassiveBox's Website
|
||||
# MassiveBox's Homepage
|
||||
|
||||
A statically generated website and blog, built with [Astro](https://astro.build/) and based on the
|
||||
[Astro Paper](https://astro-paper.pages.dev/) theme.
|
||||
A simple HTML, CSS and JQuery static homepage built with Bulma.
|
||||
|
||||
Check it out at [massive.box](https://massive.box).
|
||||
Don't forget to clone the repo with `--recurse-submodules` to get all dependancies:
|
||||
- Bulma for the base CSS of the website
|
||||
- ForkAwesome for awesome FOSS icons
|
||||
- jQuery because plain JS is boring
|
||||
- RSS Parser because Pleroma's feed is in RSS format
|
||||
|
||||
## Getting started
|
||||
|
||||
```shell
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
In order to compile the site, run
|
||||
```shell
|
||||
npm run build
|
||||
```
|
||||
The website will be built to the `./dist` folder.
|
||||
|
||||
## Blogging with SiYuan
|
||||
|
||||
You can use SiYuan to write and manage your blog posts, instead of just placing the Markdown files in the
|
||||
`src/content/blog` folder.
|
||||
|
||||
1. Install [SiYuan](https://b3log.org/siyuan/en/)
|
||||
2. Start the application and keep it running in the background
|
||||
3. Create a notebook for your blog posts, then right-click on it in the document tree, select `Settings`, then `Copy ID`
|
||||
4. Set the environment variable `BLOG_NOTEBOOK_ID` to the notebook ID you copied earlier, like so:
|
||||
`BLOG_NOTEBOOK_ID=your_notebook_id`
|
||||
5. Run `npm run siyuan`
|
||||
|
||||
The documents in your SiYuan notebook must include the frontmatter inside a code block with `yaml` as the language.
|
||||
Every line before the frontmatter will be deleted. The notes' title is entirely irrelevant, only the frontmatter
|
||||
determines the slug and title.
|
||||
|
||||
You can embed attachments in your notes, and they will work normally in the blog.
|
||||
You might need to restart the development server after running the script.
|
||||
|
||||
In order, to have an ogImage (also known as article cover):
|
||||
1. Insert the image in your note before the YAML frontmatter block, so that it gets added to the Assets folder
|
||||
2. Copy the asset link (Right-click on the image > copy `image URL`)
|
||||
3. You can now remove the image from the note, or you can keep it there. It will not be shown twice in the blog post as
|
||||
long as it's before the frontmatter.
|
||||
- Generally, it's best to keep the image referenced somewhere, so that it's not suggested as an "unused asset".
|
||||
- A simple and reasonable way to do this is to set the image as the document's cover image.
|
||||
- Hover on the cover image, click `Assets` (photograph icon), and select your cover image.
|
||||
|
||||
## Blogging with Joplin
|
||||
|
||||
If you prefer Joplin over SiYuan, that can also be used to generate the blog posts.
|
||||
|
||||
1. Install [Joplin](https://joplinapp.org/)
|
||||
2. Start the Web Clipper Service: Tools > Options > Web Clipper > Start Web Clipper Service
|
||||
3. Run `npm run joplin`
|
||||
4. Allow the request for an API token on the Joplin app
|
||||
- You can also place the Joplin token in an `.env` file in the format `JOPLIN_KEY=your_token`.
|
||||
|
||||
The script will look for a notebook named `Blog`. It will then download all notes from that notebook, alongside their
|
||||
attachments, and place them in the `src/content/blog` folder.
|
||||
|
||||
In order, to have an ogImage (also known as article cover):
|
||||
1. Add the image to the note, and change the alt text to `ogImage`. It will look like this:
|
||||
```
|
||||

|
||||
```
|
||||
2. Copy the attachment ID (in this example, it's `0f409e2b6faf44e48c5c0d99453d02c0`)
|
||||
3. Paste it in your frontmatter like this:
|
||||
```
|
||||
ogImage: ./0f409e2b6faf44e48c5c0d99453d02c0
|
||||
```
|
||||
Note the `.` (dot) instead of `:` (colon)!
|
||||
|
||||
## Uploading to WebDav
|
||||
|
||||
I have found that a quick and easy way to deploy the website is via WebDav and compatible software on the server side, like
|
||||
[this](https://github.com/mholt/caddy-webdav) plugin for Caddy.
|
||||
|
||||
To simplify uploading to WebDav, I have created a script, which can be run with `npm run upload`.
|
||||
|
||||
You have to set the following environment variables:
|
||||
- `WEBDAV_ENDPOINT`: The URL of the WebDav server, e.g. `https://example.com/webdav/`
|
||||
- `WEBDAV_USERNAME`: The username for the WebDav server
|
||||
- `WEBDAV_PASSWORD`: The password for the WebDav server
|
||||
- `WEBDAV_PATH`: The path to the folder on the server, e.g. `/public_html/` (default: `/`)
|
||||
|
||||
The script will upload the `./dist` folder, so make sure you run `npm run build` before the upload script.
|
||||
Note that running the script will clear the remote folder before uploading the files.
|
||||
|
||||
## License
|
||||
|
||||
The original [Astro Paper](https://github.com/satnaing/astro-paper) is licensed under the MIT License, Copyright © 2025
|
||||
[Sat Naing](https://github.com/satnaing).
|
||||
Any change to the original code is licensed under the AGPLv3 License, Copyright © 2025 MassiveBox.
|
||||
You can check out a live version at https://massivebox.net or https://massivebox.netlify.app/ (without the Pleroma feed since the path is relative, so it tries to fetch https://massivebox.netlify.app/feed.atom, which doesn't exist).
|
BIN
assets/avatar.jpg
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/favicon.ico
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
assets/homeicons/c.jpg
Normal file
After Width: | Height: | Size: 5 KiB |
BIN
assets/homeicons/compose.jpg
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
assets/homeicons/docker.jpg
Normal file
After Width: | Height: | Size: 7.5 KiB |
BIN
assets/homeicons/front.jpg
Normal file
After Width: | Height: | Size: 7.8 KiB |
BIN
assets/homeicons/go.jpg
Normal file
After Width: | Height: | Size: 5.1 KiB |
BIN
assets/homeicons/jamstack.jpg
Normal file
After Width: | Height: | Size: 7.2 KiB |
BIN
assets/homeicons/linux.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/homeicons/mongo.jpg
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
assets/homeicons/php.jpg
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/homeicons/py.jpg
Normal file
After Width: | Height: | Size: 4.4 KiB |
BIN
assets/homeicons/redis.jpg
Normal file
After Width: | Height: | Size: 4.1 KiB |
BIN
assets/homeicons/sql.jpg
Normal file
After Width: | Height: | Size: 4.2 KiB |
BIN
assets/maurizio.jpg
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
assets/navlogo.jpg
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/projects/2048.jpg
Normal file
After Width: | Height: | Size: 7.9 KiB |
BIN
assets/projects/deletedbot.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
assets/projects/jiljil.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
BIN
assets/projects/keroflex.jpg
Normal file
After Width: | Height: | Size: 6.4 KiB |
BIN
assets/projects/palla.jpg
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/projects/sogs.jpg
Normal file
After Width: | Height: | Size: 9.4 KiB |
BIN
assets/spinning.mp4
Normal file
BIN
assets/spinning.webm
Normal file
|
@ -1,36 +0,0 @@
|
|||
import { defineConfig } from "astro/config";
|
||||
import react from "@astrojs/react";
|
||||
import remarkToc from "remark-toc";
|
||||
import remarkCollapse from "remark-collapse";
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
import { SITE } from "./src/config";
|
||||
import "@iconify-json/fa6-solid";
|
||||
|
||||
import icon from "astro-icon";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
site: SITE.website,
|
||||
integrations: [
|
||||
react(), sitemap(),
|
||||
icon({include: {
|
||||
'fa6-solid': ["*"],
|
||||
'simple-icons': ["*"]
|
||||
}})
|
||||
],
|
||||
markdown: {
|
||||
remarkPlugins: [remarkToc, [remarkCollapse, {
|
||||
test: "Table of contents"
|
||||
}]],
|
||||
shikiConfig: {
|
||||
theme: "one-dark-pro",
|
||||
wrap: true
|
||||
}
|
||||
},
|
||||
vite: {
|
||||
optimizeDeps: {
|
||||
exclude: ["@resvg/resvg-js"]
|
||||
}
|
||||
},
|
||||
scopedStyleStrategy: "where",
|
||||
});
|
139
contact.html
Normal file
|
@ -0,0 +1,139 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<!--
|
||||
MassiveBox's Homepage
|
||||
Copyright (C) 2021 MassiveBox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Contacts - MassiveBox</title>
|
||||
<link rel="stylesheet" href="deps/bulma/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="deps/forkawesome/css/fork-awesome.min.css">
|
||||
<script src="deps/jquery/dist/jquery.min.js"></script>
|
||||
<link rel="icon" type="image/x-icon" href="assets/favicon.ico">
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="author" content="MassiveBox" />
|
||||
<meta name="description" content="MassiveBox is a free time developer and FOSS enthusiast. Here you can contact him." />
|
||||
<meta name="keywords" content="developer, web, backend, telegram, session, massive, box, massivebox" />
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
// Check for click events on the navbar burger icon
|
||||
$(".navbar-burger").click(function() {
|
||||
|
||||
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||
$(".navbar-burger").toggleClass("is-active");
|
||||
$(".navbar-menu").toggleClass("is-active");
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<section class="hero is-primary" style="padding: 2vw 7vw 2vw 7vw; background: #050732 !important">
|
||||
|
||||
<nav class="navbar" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="./">
|
||||
<img src="assets/navlogo.jpg" alt="Navigation bar logo">
|
||||
<div style="width: 15px"></div>
|
||||
<p class="is-size-4"><b><i>MassiveBox</i></b></p>
|
||||
</a>
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-end">
|
||||
<a class="navbar-item" href="./"><span class="icon-text"><span class="icon"><i class="fa fa-home"></i></span><span>Home</span></span></a>
|
||||
<a class="navbar-item" href="projects.html"><span class="icon-text"><span class="icon"><i class="fa fa-cloud"></i></span><span>Projects</span></span></a>
|
||||
<a class="navbar-item" href="https://blog.massivebox.net" target="_blank" rel="noopener noreferrer"><span class="icon-text"><span class="icon"><i class="fa fa-feed"></i></span><span>Blog</span></span></a>
|
||||
<a class="navbar-item" href="contact.html"><span class="icon-text"><span class="icon"><i class="fa fa-envelope"></i></span><span>Contact</span></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
<section class="section is-medium">
|
||||
<div id="header">
|
||||
<p class="title">Contact me</p>
|
||||
<p class="subtitle"><br>
|
||||
Feel free to contact me whenever you want, as long as you're not asking something obvious or spamming.<br>
|
||||
I don't guarantee that I will respond.
|
||||
</p>
|
||||
</div>
|
||||
<div id="contact"><br>
|
||||
<ul>
|
||||
<li><p class="subtitle"><span class="icon"><i class="fa fa-envelope"></i></span> <b>Email:</b> <a href="mailto:hello@massivebox.net">hello@massivebox.net</a></p></li>
|
||||
<li><p class="subtitle"><span class="icon"><i class="fa fa-matrix-org"></i></span> <b>Matrix:</b> <a href="https://matrix.to/#/@massivebox:massivebox.net">@massivebox:massivebox.net</a></p></li>
|
||||
<li><p class="subtitle"><span class="icon"><i class="fa fa-lock"></i></span> <b>Session</b> (ONS name): <code>massivebox</code></p></li>
|
||||
<li><p class="subtitle"><span class="icon"><i class="fa fa-telegram"></i></span> <b>Telegram:</b> <a href="https://t.me/massivebox">@MassiveBox</a></p></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div id="maurizio">
|
||||
<img src="assets/maurizio.jpg" alt="Maurizio the Cat">
|
||||
<p class="subtitle"><a href="https://t.me/ilgattomaurizio">Gatto Maurizio</a> doesn't look happy about you being a robot.</p>
|
||||
<button class="button is-success is-light" onclick="showcontact();">I'm not a robot anymore, Maurizio please spare me</button>
|
||||
</div>
|
||||
<div id="riddle">
|
||||
<p class="subtitle"><br><b>Riddle time:</b> Are you a robot?</p>
|
||||
<button class="button is-success is-light" onclick="showmaurizio();">Yes</button>
|
||||
<button class="button is-danger is-light" onclick="showcontact();">No</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
$("#maurizio").hide();
|
||||
$("#contact").hide();
|
||||
|
||||
function showmaurizio() {
|
||||
$("#maurizio").show();
|
||||
$("#header").hide();
|
||||
$("#riddle").hide();
|
||||
}
|
||||
|
||||
function showcontact() {
|
||||
$("#header").show();
|
||||
$("#maurizio").hide();
|
||||
$("#contact").show();
|
||||
$("#riddle").hide();
|
||||
}
|
||||
</script>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
Website by MassiveBox, handcrafted in Italy with <span class="icon has-text-danger"><i class="fa fa-heart"></i></span>.<br>
|
||||
All logos in this page are copyright of their owner.<br>
|
||||
<a href="https://gitea.massivebox.net/massivebox/website" target="_blank" rel="noopener noreferrer">Source code</a> | <a href="https://ecodash.massivebox.net" target="_blank" rel="noopener noreferrer">Environmental Report</a> | <a href="/pages/privacy.html" target="_blank" rel="noopener noreferrer">Privacy Policy</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
50
custom.scss
|
@ -1,50 +0,0 @@
|
|||
$main-color: #001440;
|
||||
$main-color-dark: #071022;
|
||||
|
||||
$navbar-background-color: $main-color !important;
|
||||
$bulma-hover-background-l-delta: 0;
|
||||
|
||||
|
||||
@import "bulma/sass";
|
||||
|
||||
@mixin default-page-content-spacing {
|
||||
width: 100%;
|
||||
max-width: 46rem;
|
||||
padding: 3rem 1rem 1rem 1rem;
|
||||
}
|
||||
|
||||
#main-content {
|
||||
@include default-page-content-spacing;
|
||||
margin: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
nav.navbar {
|
||||
padding-left: 5vw;
|
||||
padding-right: 5vw;
|
||||
height: 10vh;
|
||||
min-height: 65px;
|
||||
}
|
||||
nav.navbar * {
|
||||
color: white;
|
||||
}
|
||||
a.navbar-item:hover, a.navbar-item:active, a.navbar-item:focus {
|
||||
background-color: #05122e;
|
||||
}
|
||||
|
||||
.is-background-main { background-color: $main-color; }
|
||||
|
||||
|
||||
@media (max-width: 1023px) {
|
||||
nav.navbar {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
height: 8vh;
|
||||
}
|
||||
.navbar-brand {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
1
deps/bulma
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 64f739c0c939b5dedbf8dfb4595f7192d1e766a5
|
1
deps/forkawesome
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit a99579ae3e735ee70e51ed62dfcee3172b5b2db7
|
1
deps/jquery
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 6ad26fc72ddbc9d3f3886f98164efabc112d6c78
|
1
deps/rssparser
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit e2e2f4d03a74811fd4dc624381b0f336bcdde962
|
|
@ -1,11 +0,0 @@
|
|||
version: '3'
|
||||
|
||||
services:
|
||||
app:
|
||||
image: node:18
|
||||
ports:
|
||||
- 4321:4321
|
||||
working_dir: /app
|
||||
command: npm run dev -- --host 0.0.0.0
|
||||
volumes:
|
||||
- ./:/app
|
323
index.html
Normal file
|
@ -0,0 +1,323 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<!--
|
||||
MassiveBox's Homepage
|
||||
Copyright (C) 2021 MassiveBox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Home - MassiveBox</title>
|
||||
<link rel="stylesheet" href="deps/bulma/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="deps/forkawesome/css/fork-awesome.min.css">
|
||||
<script src="deps/jquery/dist/jquery.min.js"></script>
|
||||
<link rel="icon" type="image/x-icon" href="assets/favicon.ico">
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="author" content="MassiveBox" />
|
||||
<meta name="description" content="MassiveBox is a free time developer and FOSS enthusiast. This is his website." />
|
||||
<meta name="keywords" content="developer, web, backend, telegram, session, massive, box, massivebox" />
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
// Check for click events on the navbar burger icon
|
||||
$(".navbar-burger").click(function() {
|
||||
|
||||
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||
$(".navbar-burger").toggleClass("is-active");
|
||||
$(".navbar-menu").toggleClass("is-active");
|
||||
|
||||
});
|
||||
|
||||
var dob = new Date("09/03/2004");
|
||||
var month_diff = Date.now() - dob.getTime();
|
||||
var age_dt = new Date(month_diff);
|
||||
var year = age_dt.getUTCFullYear();
|
||||
$("#age").text(Math.abs(year - 1970));
|
||||
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<section class="hero is-primary is-fullheight" style="padding: 2vw 7vw 2vw 7vw; background: #050732 !important">
|
||||
|
||||
<nav class="navbar" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="./">
|
||||
<img src="assets/navlogo.jpg" alt="Navigation bar logo">
|
||||
<div style="width: 15px"></div>
|
||||
<p class="is-size-4"><b><i>MassiveBox</i></b></p>
|
||||
</a>
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-end">
|
||||
<a class="navbar-item" href="./"><span class="icon-text"><span class="icon"><i class="fa fa-home"></i></span><span>Home</span></span></a>
|
||||
<a class="navbar-item" href="projects.html"><span class="icon-text"><span class="icon"><i class="fa fa-cloud"></i></span><span>Projects</span></span></a>
|
||||
<a class="navbar-item" href="https://blog.massivebox.net" target="_blank" rel="noopener noreferrer"><span class="icon-text"><span class="icon"><i class="fa fa-feed"></i></span><span>Blog</span></span></a>
|
||||
<a class="navbar-item" href="contact.html"><span class="icon-text"><span class="icon"><i class="fa fa-envelope"></i></span><span>Contact</span></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
|
||||
<div class="hero-body">
|
||||
<table style="width: 100%">
|
||||
<tr style="text-align:center">
|
||||
<td><p>
|
||||
<video autoplay loop muted playsinline class="is-hidden-touch" style="width: 257px">
|
||||
<source src="assets/spinning.webm" type="video/webm">
|
||||
<source src="assets/spinning.mp4" type="video/mp4">
|
||||
</video>
|
||||
</p></td><!-- Desktop -->
|
||||
<td>
|
||||
<div>
|
||||
<p>
|
||||
<video autoplay loop muted playsinline class="is-hidden-desktop" style="width: 257px">
|
||||
<source src="assets/spinning.webm" type="video/webm">
|
||||
<source src="assets/spinning.mp4" type="video/mp4">
|
||||
</video>
|
||||
</p><!-- Mobile -->
|
||||
|
||||
<p class="title" style="font-size: 9vw;"><b><i>MassiveBox</i></b></p><!-- Desktop -->
|
||||
|
||||
<p class="is-size-5">Yup. That's me.</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<p class="title">Who am I</p>
|
||||
<p class="subtitle">
|
||||
I'm Matteo, a <span id="age"></span> years old guy from Brescia, Italy.<br>
|
||||
In my free time I like fiddling around with code, and sometimes I come up with something decent that I publish to the world.<br>
|
||||
I like cats, Linux, Free and Open Source Software, and privacy.
|
||||
</p>
|
||||
<a class="button is-link" href="https://codeberg.org/massivebox">Codeberg</a>
|
||||
<a class="button is-link" rel="me" href="https://blob.cat/massivebox">Fediverse</a>
|
||||
<a class="button is-link" href="https://blog.massivebox.net">Blog</a>
|
||||
</section>
|
||||
|
||||
<section class="hero is-medium is-link">
|
||||
<div class="hero-body">
|
||||
<p class="title">Stuff I know (kind of)</p>
|
||||
<div class="subtitle columns">
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/go.jpg" alt="go's logo" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>Go</b></p>
|
||||
<p>Backend work, Telegram bots, API endpoints</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/front.jpg" alt="html, javascript, css" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>HTML, CSS, JavaScript/JQuery</b></p>
|
||||
<p>Frontend work</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/linux.jpg" alt="linux tux" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>Linux</b></p>
|
||||
<p>Runs on all my PCs and my server</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/c.jpg" alt="c logo" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>C</b></p>
|
||||
<p>Stuff with SDL1 and SDL2, mostly 2D games</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/py.jpg" alt="python logo" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>Python</b></p>
|
||||
<p>Simple scripts for myself, internal stuff</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/sql.jpg" alt="mysql logo" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>MySQL</b></p>
|
||||
<p>Database engine of most of my projects</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subtitle columns">
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/docker.jpg" alt="docker logo" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>Docker</b></p>
|
||||
<p>All my projects run on Docker</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/compose.jpg" alt="compose logo" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>Docker Compose</b></p>
|
||||
<p>Making Docker easy.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/redis.jpg" alt="redis logo" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>Redis</b></p>
|
||||
<p>Database engine for my simplest projects</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/mongo.jpg" alt="mongodb logo" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>MongoDB</b></p>
|
||||
<p>Database engine of some of my projects</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/php.jpg" alt="php logo" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>PHP</b></p>
|
||||
<p>It was a long time ago I swear</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-2">
|
||||
<div class="card" style="height: 100%">
|
||||
<div class="card-image">
|
||||
<figure class="image is-1by1"><img src="assets/homeicons/jamstack.jpg" alt="jamstack image" loading="lazy"></figure>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<p><b>Jamstack</b></p>
|
||||
<p>It's my way of life.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<p class="title">What's happening</p>
|
||||
<p>Feed from my <a href="https://blob.cat/massivebox">Fediverse account</a></p>
|
||||
<div id="mastodonfeed" style="overflow-x: hidden; overflow-y: auto; height: 50vh; margin-top: 5px">
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script src="deps/rssparser/dist/rss-parser.min.js"></script>
|
||||
<script>
|
||||
|
||||
let parser = new RSSParser();
|
||||
parser.parseURL('/feed.atom', function(err, feed) {
|
||||
if (err) {
|
||||
$("#mastodonfeed").html(`<p><span class="icon has-text-danger"><i class="fa fa-times"></i></span> Error parsing Pleroma RSS feed. Try refreshing the page.</p>`);
|
||||
}
|
||||
feed.items.forEach(function(entry) {
|
||||
date = new Date(Date.parse(entry.isoDate));
|
||||
html = `
|
||||
<div class="card mb-3">
|
||||
<article class="media" style="padding: 10px">
|
||||
<figure class="media-left">
|
||||
<p class="image is-64x64">
|
||||
<a href="`+entry.link+`"><img src="assets/avatar.jpg" alt="My profile picture"></a>
|
||||
</p>
|
||||
</figure>
|
||||
<div class="media-content">
|
||||
<div class="content">
|
||||
<p><strong>MassiveBox</strong> <small><a href="https://blob.cat/massivebox" target="_blank" rel="noopener noreferrer">massivebox@blob.cat</a></small> <small>`+date.getDate()+"/"+(date.getMonth()+1)+"/"+date.getFullYear()+`</small></p>
|
||||
`+entry.content.replace(/:(\S*):/gm, '<img src="/emoji/$1" width="16" alt="$1" loading="lazy">')+`
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>`;
|
||||
$("#mastodonfeed").html($("#mastodonfeed").html()+html);
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
Website by MassiveBox, handcrafted in Italy with <span class="icon has-text-danger"><i class="fa fa-heart"></i></span>.<br>
|
||||
All logos in this page are copyright of their owner.<br>
|
||||
<a href="https://gitea.massivebox.net/massivebox/website" target="_blank" rel="noopener noreferrer">Source code</a> | <a href="https://ecodash.massivebox.net" target="_blank" rel="noopener noreferrer">Environmental Report</a> | <a href="/pages/privacy.html" target="_blank" rel="noopener noreferrer">Privacy Policy</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
14471
package-lock.json
generated
75
package.json
|
@ -1,75 +0,0 @@
|
|||
{
|
||||
"name": "website",
|
||||
"version": "4.2.0",
|
||||
"scripts": {
|
||||
"dev": "astro dev --host",
|
||||
"start": "astro dev",
|
||||
"build": "astro check && astro build && jampack ./dist",
|
||||
"preview": "astro preview",
|
||||
"sync": "astro sync",
|
||||
"astro": "astro",
|
||||
"format:check": "prettier --check . --plugin=prettier-plugin-astro",
|
||||
"format": "prettier --write . --plugin=prettier-plugin-astro",
|
||||
"cz": "cz",
|
||||
"prepare": "husky install",
|
||||
"lint": "eslint .",
|
||||
"siyuan": "node src/utils/siyuan.mjs",
|
||||
"joplin": "node src/utils/joplin.mjs",
|
||||
"upload": "node src/utils/upload.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.7.0",
|
||||
"@astrojs/rss": "^4.0.2",
|
||||
"@iconify-json/fa6-brands": "^1.1.19",
|
||||
"@iconify-json/simple-icons": "^1.1.105",
|
||||
"@resvg/resvg-js": "^2.6.0",
|
||||
"astro": "^4.2.1",
|
||||
"astro-icon": "^1.1.0",
|
||||
"bulma": "^1.0.1",
|
||||
"fuse.js": "^7.0.0",
|
||||
"github-slugger": "^2.0.0",
|
||||
"marked": "^13.0.2",
|
||||
"react-bulma-components": "^4.1.0",
|
||||
"remark-collapse": "^0.1.2",
|
||||
"remark-toc": "^9.0.0",
|
||||
"sass": "^1.77.2",
|
||||
"satori": "^0.10.11",
|
||||
"tailwindcss": "^3.4.1",
|
||||
"typescript": "^5.4.5",
|
||||
"webdav": "^5.8.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/react": "^3.0.9",
|
||||
"@astrojs/sitemap": "^3.0.5",
|
||||
"@astrojs/tailwind": "^5.1.0",
|
||||
"@divriots/jampack": "^0.23.2",
|
||||
"@iconify-json/fa6-solid": "^1.2.3",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@types/github-slugger": "^1.3.0",
|
||||
"@types/react": "^18.2.48",
|
||||
"@typescript-eslint/parser": "^6.19.0",
|
||||
"astro-eslint-parser": "^0.16.2",
|
||||
"commitizen": "^4.3.0",
|
||||
"cz-conventional-changelog": "^3.3.0",
|
||||
"dotenv": "^16.4.7",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-plugin-astro": "^0.31.3",
|
||||
"husky": "^8.0.3",
|
||||
"lint-staged": "^15.2.0",
|
||||
"prettier": "^3.2.4",
|
||||
"prettier-plugin-astro": "^0.13.0",
|
||||
"prettier-plugin-tailwindcss": "^0.5.11",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"config": {
|
||||
"commitizen": {
|
||||
"path": "./node_modules/cz-conventional-changelog"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,jsx,ts,tsx,md,mdx,json,astro}": [
|
||||
"prettier --write --plugin=prettier-plugin-astro"
|
||||
]
|
||||
}
|
||||
}
|
133
projects.html
Normal file
|
@ -0,0 +1,133 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<!--
|
||||
MassiveBox's Homepage
|
||||
Copyright (C) 2021 MassiveBox
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Projects - MassiveBox</title>
|
||||
<link rel="stylesheet" href="deps/bulma/css/bulma.min.css">
|
||||
<link rel="stylesheet" href="deps/forkawesome/css/fork-awesome.min.css">
|
||||
<script src="deps/jquery/dist/jquery.min.js"></script>
|
||||
<link rel="icon" type="image/x-icon" href="assets/favicon.ico">
|
||||
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="author" content="MassiveBox" />
|
||||
<meta name="description" content="MassiveBox is a free time developer and FOSS enthusiast. Here you can see his projects." />
|
||||
<meta name="keywords" content="developer, web, backend, telegram, session, massive, box, massivebox" />
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
// Check for click events on the navbar burger icon
|
||||
$(".navbar-burger").click(function() {
|
||||
|
||||
// Toggle the "is-active" class on both the "navbar-burger" and the "navbar-menu"
|
||||
$(".navbar-burger").toggleClass("is-active");
|
||||
$(".navbar-menu").toggleClass("is-active");
|
||||
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
|
||||
<section class="hero is-primary" style="padding: 2vw 7vw 2vw 7vw; background: #050732 !important">
|
||||
|
||||
<nav class="navbar" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="./">
|
||||
<img src="assets/navlogo.jpg" alt="Navigation bar logo">
|
||||
<div style="width: 15px"></div>
|
||||
<p class="is-size-4"><b><i>MassiveBox</i></b></p>
|
||||
</a>
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarBasicExample" class="navbar-menu">
|
||||
<div class="navbar-end">
|
||||
<a class="navbar-item" href="./"><span class="icon-text"><span class="icon"><i class="fa fa-home"></i></span><span>Home</span></span></a>
|
||||
<a class="navbar-item" href="projects.html"><span class="icon-text"><span class="icon"><i class="fa fa-cloud"></i></span><span>Projects</span></span></a>
|
||||
<a class="navbar-item" href="https://blog.massivebox.net" target="_blank" rel="noopener noreferrer"><span class="icon-text"><span class="icon"><i class="fa fa-feed"></i></span><span>Blog</span></span></a>
|
||||
<a class="navbar-item" href="contact.html"><span class="icon-text"><span class="icon"><i class="fa fa-envelope"></i></span><span>Contact</span></span></a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</section>
|
||||
|
||||
<section class="hero is-medium is-link">
|
||||
<div class="hero-body">
|
||||
<p class="title" style="text-align:center">My Projects</p>
|
||||
<p class="subtitle" style="text-align:center">Click on them to see more details</p>
|
||||
<div class="container">
|
||||
<div id="projects" style="background-color: rgb(243, 243, 243); border-radius: 10px; padding: 15px">
|
||||
</div>
|
||||
<p><br>Fetched from <a href="https://gitea.massivebox.net/massivebox" target="_blank" rel="noopener noreferrer">my Gitea</a>. Check me out on <a href="https://codeberg.org/massivebox" target="_blank" rel="noopener noreferrer">Codeberg</a> and <a href="https://github.com/massivebox" target="_blank" rel="noopener noreferrer">GitHub</a> as well!</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
|
||||
$.getJSON("https://massivebox.net/gitea-repos", function(data, textStatus) {
|
||||
if(textStatus != "success") {
|
||||
$("#projects").html(`<p><span class="icon has-text-danger"><i class="fa fa-times"></i></span> Error parsing Gitea repositories. Try refreshing the page.</p>`)
|
||||
}
|
||||
data.forEach(function(entry) {
|
||||
html = `
|
||||
<div class="card mb-3" style="padding: 20px 10px 20px 10px">
|
||||
<article class="media">
|
||||
<div class="media-content">
|
||||
<a href="`+entry.html_url+`" target="_blank" rel="noopener noreferrer">
|
||||
<div class="content">
|
||||
<nav class="level">
|
||||
<h3 style="color: #4183c4">`+entry.name+`</h3>
|
||||
</nav>
|
||||
<p>`+entry.description+`</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
</div>`;
|
||||
$("#projects").html($("#projects").html()+html);
|
||||
})
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
Website by MassiveBox, handcrafted in Italy with <span class="icon has-text-danger"><i class="fa fa-heart"></i></span>.<br>
|
||||
All logos in this page are copyright of their owner.<br>
|
||||
<a href="https://gitea.massivebox.net/massivebox/website" target="_blank" rel="noopener noreferrer">Source code</a> | <a href="https://ecodash.massivebox.net" target="_blank" rel="noopener noreferrer">Environmental Report</a> | <a href="/pages/privacy.html" target="_blank" rel="noopener noreferrer">Privacy Policy</a>
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 1.4 MiB |
Before Width: | Height: | Size: 1.8 MiB |
Before Width: | Height: | Size: 1.2 MiB |
|
@ -1,9 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#050732</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 15 KiB |
|
@ -1,13 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 36 36">
|
||||
<path fill="#000" d="M22.25 4h-8.5a1 1 0 0 0-.96.73l-5.54 19.4a.5.5 0 0 0 .62.62l5.05-1.44a2 2 0 0 0 1.38-1.4l3.22-11.66a.5.5 0 0 1 .96 0l3.22 11.67a2 2 0 0 0 1.38 1.39l5.05 1.44a.5.5 0 0 0 .62-.62l-5.54-19.4a1 1 0 0 0-.96-.73Z"/>
|
||||
<path fill="url(#gradient)" d="M18 28a7.63 7.63 0 0 1-5-2c-1.4 2.1-.35 4.35.6 5.55.14.17.41.07.47-.15.44-1.8 2.93-1.22 2.93.6 0 2.28.87 3.4 1.72 3.81.34.16.59-.2.49-.56-.31-1.05-.29-2.46 1.29-3.25 3-1.5 3.17-4.83 2.5-6-.67.67-2.6 2-5 2Z"/>
|
||||
<defs>
|
||||
<linearGradient id="gradient" x1="16" x2="16" y1="32" y2="24" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#000"/>
|
||||
<stop offset="1" stop-color="#000" stop-opacity="0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<style>
|
||||
@media (prefers-color-scheme:dark){:root{filter:invert(100%)}}
|
||||
</style>
|
||||
</svg>
|
Before Width: | Height: | Size: 873 B |
Before Width: | Height: | Size: 14 KiB |
BIN
public/og.webp
Before Width: | Height: | Size: 42 KiB |
|
@ -1,36 +0,0 @@
|
|||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="1024.000000pt" height="1024.000000pt" viewBox="0 0 1024.000000 1024.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.14, written by Peter Selinger 2001-2017
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,1024.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M5020 10218 c-109 -27 -410 -108 -445 -119 -16 -5 -100 -30 -185 -55
|
||||
-287 -84 -730 -241 -915 -324 -27 -12 -59 -26 -70 -30 -183 -76 -469 -219
|
||||
-698 -349 -211 -119 -514 -320 -732 -484 -88 -67 -165 -126 -171 -131 -6 -6
|
||||
-26 -22 -44 -36 -18 -14 -38 -29 -44 -35 -6 -5 -51 -44 -101 -85 -183 -152
|
||||
-378 -333 -665 -618 -135 -133 -253 -257 -263 -275 -28 -54 -200 -699 -241
|
||||
-907 -7 -33 -56 -279 -62 -305 -2 -10 -6 -35 -9 -55 -3 -20 -8 -47 -10 -60 -6
|
||||
-30 -22 -130 -30 -185 -3 -22 -8 -51 -10 -65 -3 -14 -7 -50 -10 -80 -3 -30 -8
|
||||
-66 -10 -80 -2 -14 -6 -52 -9 -85 -3 -33 -9 -85 -12 -115 -30 -293 -35 -870
|
||||
-9 -1130 2 -19 6 -69 9 -110 4 -41 8 -91 11 -110 3 -19 7 -55 10 -80 10 -94
|
||||
38 -301 50 -370 3 -14 12 -65 21 -115 70 -404 274 -1216 322 -1282 11 -15 139
|
||||
-145 285 -288 278 -273 298 -293 442 -420 50 -44 92 -83 95 -86 4 -4 271 -225
|
||||
330 -272 8 -7 60 -46 115 -88 592 -445 1189 -765 1920 -1029 427 -154 1174
|
||||
-366 1255 -356 44 6 484 122 650 172 213 64 418 131 569 186 727 261 1361 604
|
||||
1941 1047 85 65 221 172 230 181 3 3 32 28 65 55 118 98 168 142 340 299 157
|
||||
142 583 567 608 606 13 19 33 71 46 115 31 109 121 444 127 475 3 14 14 59 24
|
||||
101 16 62 51 216 76 334 43 204 105 581 120 735 2 25 6 61 9 80 2 19 7 71 11
|
||||
115 4 44 8 94 10 110 7 69 16 371 16 515 0 155 -11 480 -17 490 -1 3 -6 52
|
||||
-10 109 -3 57 -8 111 -10 120 -1 9 -6 48 -9 86 -4 39 -11 95 -16 125 -5 30
|
||||
-11 76 -15 103 -9 77 -53 324 -81 458 -14 68 -27 133 -29 144 -50 240 -213
|
||||
856 -242 911 -15 29 -408 426 -568 574 -185 171 -375 336 -510 443 -33 27 -76
|
||||
61 -95 76 -19 16 -69 54 -110 84 -41 31 -79 60 -85 64 -25 21 -156 112 -277
|
||||
192 -582 386 -1201 671 -1988 915 -80 25 -170 51 -200 60 -30 8 -87 24 -126
|
||||
35 -159 46 -431 114 -464 116 -19 1 -69 -6 -110 -17z"/>
|
||||
</g>
|
||||
</svg>
|
Before Width: | Height: | Size: 2.2 KiB |
|
@ -1,19 +0,0 @@
|
|||
{
|
||||
"name": "MassiveBox",
|
||||
"short_name": "MassiveBox",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#050732",
|
||||
"background_color": "#050732",
|
||||
"display": "standalone"
|
||||
}
|
|
@ -1,76 +0,0 @@
|
|||
const primaryColorScheme = ""; // "light" | "dark"
|
||||
|
||||
// Get theme data from local storage
|
||||
const currentTheme = localStorage.getItem("theme");
|
||||
|
||||
function getPreferTheme() {
|
||||
// return theme value in local storage if it is set
|
||||
if (currentTheme) return currentTheme;
|
||||
|
||||
// return primary color scheme if it is set
|
||||
if (primaryColorScheme) return primaryColorScheme;
|
||||
|
||||
// return user device's prefer color scheme
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light";
|
||||
}
|
||||
|
||||
let themeValue = getPreferTheme();
|
||||
|
||||
function setPreference() {
|
||||
localStorage.setItem("theme", themeValue);
|
||||
reflectPreference();
|
||||
}
|
||||
|
||||
function reflectPreference() {
|
||||
document.firstElementChild.setAttribute("data-theme", themeValue);
|
||||
|
||||
document.querySelector("#theme-btn")?.setAttribute("aria-label", themeValue);
|
||||
|
||||
// Get a reference to the body element
|
||||
const body = document.body;
|
||||
|
||||
// Check if the body element exists before using getComputedStyle
|
||||
if (body) {
|
||||
// Get the computed styles for the body element
|
||||
const computedStyles = window.getComputedStyle(body);
|
||||
|
||||
// Get the background color property
|
||||
const bgColor = computedStyles.backgroundColor;
|
||||
|
||||
// Set the background color in <meta theme-color ... />
|
||||
document
|
||||
.querySelector("meta[name='theme-color']")
|
||||
?.setAttribute("content", bgColor);
|
||||
}
|
||||
}
|
||||
|
||||
// set early so no page flashes / CSS is made aware
|
||||
reflectPreference();
|
||||
|
||||
window.onload = () => {
|
||||
function setThemeFeature() {
|
||||
// set on load so screen readers can get the latest value on the button
|
||||
reflectPreference();
|
||||
|
||||
// now this script can find and listen for clicks on the control
|
||||
document.querySelector("#theme-btn")?.addEventListener("click", () => {
|
||||
themeValue = themeValue === "light" ? "dark" : "light";
|
||||
setPreference();
|
||||
});
|
||||
}
|
||||
|
||||
setThemeFeature();
|
||||
|
||||
// Runs on view transitions navigation
|
||||
document.addEventListener("astro:after-swap", setThemeFeature);
|
||||
};
|
||||
|
||||
// sync with system changes
|
||||
window
|
||||
.matchMedia("(prefers-color-scheme: dark)")
|
||||
.addEventListener("change", ({ matches: isDark }) => {
|
||||
themeValue = isDark ? "dark" : "light";
|
||||
setPreference();
|
||||
});
|
1
remark-collapse.d.ts
vendored
|
@ -1 +0,0 @@
|
|||
declare module 'remark-collapse';
|
|
@ -1,58 +0,0 @@
|
|||
---
|
||||
import Datetime from "./Datetime.astro";
|
||||
import { Card, Heading } from "react-bulma-components";
|
||||
import { slugifyStr } from '../utils/slugify.js';
|
||||
import { SITE } from '../config.js';
|
||||
import Tag from "./Tag.astro";
|
||||
|
||||
interface Frontmatter {
|
||||
title: string;
|
||||
pubDatetime: Date | string;
|
||||
modDatetime?: Date | null | string;
|
||||
description: string;
|
||||
ogImage?: string | object;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
href?: string;
|
||||
frontmatter: Frontmatter;
|
||||
secHeading?: boolean;
|
||||
}
|
||||
|
||||
const { href, frontmatter } = Astro.props;
|
||||
|
||||
function getOgImageUrl(frontmatter: Frontmatter): string {
|
||||
if (typeof frontmatter.ogImage === 'string') {
|
||||
return frontmatter.ogImage !== ''
|
||||
? frontmatter.ogImage
|
||||
: SITE.ogImage;
|
||||
}
|
||||
if (typeof frontmatter.ogImage === 'object' && 'src' in frontmatter.ogImage) {
|
||||
return (frontmatter.ogImage as { src: string }).src;
|
||||
}
|
||||
return SITE.ogImage;
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
<a href={href}>
|
||||
<Card className={"view-transition-" + slugifyStr(frontmatter.title) + " mb-3" } >
|
||||
<div class="card-image">
|
||||
<figure class="image">
|
||||
<img
|
||||
src={getOgImageUrl(frontmatter)}
|
||||
alt="Article image preview"
|
||||
onerror='this.onerror = null; this.src="./og.webp"'
|
||||
fetchpriority="low"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
<Card.Content>
|
||||
<Heading size={3}>{frontmatter.title}</Heading>
|
||||
{frontmatter.tags.map(tag => <Tag tag={slugifyStr(tag)} />)}
|
||||
<Datetime pubDatetime={frontmatter.pubDatetime} modDatetime={frontmatter.modDatetime} className="mb-1" />
|
||||
<p>{frontmatter.description}</p>
|
||||
</Card.Content>
|
||||
</Card>
|
||||
</a>
|
|
@ -1,72 +0,0 @@
|
|||
---
|
||||
// Remove current url path and remove trailing slash if exists
|
||||
const currentUrlPath = Astro.url.pathname.replace(/\/+$/, "");
|
||||
|
||||
// Get url array from path
|
||||
// eg: /tags/tailwindcss => ['tags', 'tailwindcss']
|
||||
const breadcrumbList = currentUrlPath.split("/").slice(1);
|
||||
|
||||
// if breadcrumb is Home > Posts > 1 <etc>
|
||||
// replace Posts with Posts (page number)
|
||||
breadcrumbList[0] === "blog" &&
|
||||
breadcrumbList.splice(0, 2, `Posts (page ${breadcrumbList[1] || 1})`);
|
||||
|
||||
// if breadcrumb is Home > Tags > [tag] > [page] <etc>
|
||||
// replace [tag] > [page] with [tag] (page number)
|
||||
breadcrumbList[0] === "tags" &&
|
||||
!isNaN(Number(breadcrumbList[2])) &&
|
||||
breadcrumbList.splice(
|
||||
1,
|
||||
3,
|
||||
`${breadcrumbList[1]} ${
|
||||
Number(breadcrumbList[2]) === 1 ? "" : "(page " + breadcrumbList[2] + ")"
|
||||
}`
|
||||
);
|
||||
---
|
||||
|
||||
<nav class="breadcrumb" aria-label="breadcrumb">
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/">Home</a>
|
||||
<span aria-hidden="true">»</span>
|
||||
</li>
|
||||
{
|
||||
breadcrumbList.map((breadcrumb, index) =>
|
||||
index + 1 === breadcrumbList.length ? (
|
||||
<li>
|
||||
<span
|
||||
class={`${index > 0 ? "lowercase" : "capitalize"}`}
|
||||
aria-current="page"
|
||||
>
|
||||
{/* make the last part lowercase in Home > Tags > some-tag */}
|
||||
{decodeURIComponent(breadcrumb)}
|
||||
</span>
|
||||
</li>
|
||||
) : (
|
||||
<li>
|
||||
<a href={`/${breadcrumb}/`}>{breadcrumb}</a>
|
||||
<span aria-hidden="true">»</span>
|
||||
</li>
|
||||
)
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<style>
|
||||
.breadcrumb {
|
||||
@apply mx-auto mb-1 mt-8 w-full max-w-3xl px-4;
|
||||
}
|
||||
.breadcrumb ul li {
|
||||
@apply inline;
|
||||
}
|
||||
.breadcrumb ul li a {
|
||||
@apply capitalize opacity-70;
|
||||
}
|
||||
.breadcrumb ul li span {
|
||||
@apply opacity-70;
|
||||
}
|
||||
.breadcrumb ul li:not(:last-child) a {
|
||||
@apply hover:opacity-100;
|
||||
}
|
||||
</style>
|
|
@ -1,28 +0,0 @@
|
|||
---
|
||||
import { LOCALE } from "@config";
|
||||
import { Icon } from "astro-icon/components";
|
||||
export interface Props {
|
||||
modDatetime?: string | null | Date;
|
||||
pubDatetime: string | Date;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const { modDatetime, pubDatetime, className = "" } = Astro.props;
|
||||
|
||||
function formatToMonthDayYear(timeString: string | Date) {
|
||||
const date = new Date(timeString);
|
||||
return date.toLocaleDateString(LOCALE.langTag, {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
|
||||
---
|
||||
|
||||
<div class={ className }>
|
||||
<Icon name="fa6-solid:calendar-days" title="Published" class="mr-1" /> <span>{ formatToMonthDayYear(pubDatetime) }</span>
|
||||
{ modDatetime && modDatetime > pubDatetime && (
|
||||
<Icon name="fa6-solid:pencil" title="Modified" class="ml-2 mr-1" /> <span>{ formatToMonthDayYear(modDatetime) } </span>
|
||||
) }
|
||||
</div>
|
|
@ -1,27 +0,0 @@
|
|||
---
|
||||
import { Footer as BulmaFooter, Container } from 'react-bulma-components';
|
||||
import { Icon } from 'astro-icon/components'
|
||||
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
export interface Props {
|
||||
noMarginTop?: boolean;
|
||||
}
|
||||
|
||||
const { noMarginTop = false } = Astro.props;
|
||||
---
|
||||
|
||||
<BulmaFooter style={{"marginTop": `${noMarginTop ? "0px" : "3rem"}`}}>
|
||||
<Container>
|
||||
<p style="text-align: center">
|
||||
© { currentYear } MassiveBox<br>
|
||||
<a href="https://git.massive.box/massivebox/website" target="_blank" rel="noopener noreferrer">Source Code</a> | <a href="https://status.massive.box" target="_blank" rel="noopener noreferrer">Server Status</a> | <a href="/pages/www-legal">Privacy & Legal</a>
|
||||
</p>
|
||||
</Container>
|
||||
</BulmaFooter>
|
||||
|
||||
<style>
|
||||
a {
|
||||
color: grey;
|
||||
}
|
||||
</style>
|
|
@ -1,45 +0,0 @@
|
|||
---
|
||||
import { Navbar } from "react-bulma-components"
|
||||
import { Icon } from 'astro-icon/components'
|
||||
---
|
||||
|
||||
<Navbar id="navbar">
|
||||
<Navbar.Brand>
|
||||
<Navbar.Item href="/">
|
||||
<img src="/assets/icon-64.webp" alt="Navigation bar logo" style="width: auto">
|
||||
<div style="width: 15px"></div>
|
||||
<p class="is-size-4"><b><i>MassiveBox</i></b></p>
|
||||
</Navbar.Item>
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navMenu">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</Navbar.Brand>
|
||||
|
||||
<Navbar.Menu id="navMenu">
|
||||
<Navbar.Container align="right">
|
||||
<Navbar.Item href="/"><span class="icon-text"><span class="icon"><Icon name="fa6-solid:house" /></span><span>Home</span></span></Navbar.Item>
|
||||
<Navbar.Item href="/about"><span class="icon-text"><span class="icon"><Icon name="fa6-solid:user" /></span><span>About</span></span></Navbar.Item>
|
||||
<Navbar.Item href="/blog"><span class="icon-text"><span class="icon"><Icon name="fa6-solid:rss" /></span><span>Blog</span></span></Navbar.Item>
|
||||
<Navbar.Item id="theme-btn"><span class="icon-text"><span class="icon"><Icon name="fa6-solid:lightbulb" /></span><span class="is-hidden-desktop">Toggle Theme</span></span></Navbar.Item>
|
||||
</Navbar.Container>
|
||||
</Navbar.Menu>
|
||||
</Navbar>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||
$navbarBurgers.forEach( el => {
|
||||
el.addEventListener('click', () => {
|
||||
const target = el.dataset.target;
|
||||
const $target = document.getElementById(target);
|
||||
el.classList.toggle('is-active');
|
||||
if($target != null) {
|
||||
$target.classList.toggle('is-active');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
export interface Props {
|
||||
noPadding?: boolean;
|
||||
ariaHidden?: boolean;
|
||||
}
|
||||
|
||||
const { noPadding = false, ariaHidden = true } = Astro.props;
|
||||
---
|
||||
|
||||
<div class={`max-w-3xl mx-auto ${noPadding ? "px-0" : "px-4"}`}>
|
||||
<hr class="border-skin-line" aria-hidden={ariaHidden} />
|
||||
</div>
|
|
@ -1,33 +0,0 @@
|
|||
---
|
||||
export interface Props {
|
||||
href: string;
|
||||
className?: string;
|
||||
ariaLabel?: string;
|
||||
title?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const { href, className, ariaLabel, title, disabled = false } = Astro.props;
|
||||
---
|
||||
|
||||
{
|
||||
disabled ? (
|
||||
<span
|
||||
class={`group inline-block ${className}`}
|
||||
aria-label={ariaLabel}
|
||||
title={title}
|
||||
aria-disabled={disabled}
|
||||
>
|
||||
<slot />
|
||||
</span>
|
||||
) : (
|
||||
<a
|
||||
{href}
|
||||
class={`group inline-block hover:text-skin-accent ${className}`}
|
||||
aria-label={ariaLabel}
|
||||
title={title}
|
||||
>
|
||||
<slot />
|
||||
</a>
|
||||
)
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
<script defer src="https://stats.massive.box/p.js" data-website-id="30344937-ffdf-4a6a-ba52-dc60a23aa4f2"></script>
|
|
@ -1,55 +0,0 @@
|
|||
---
|
||||
import LinkButton from "./LinkButton.astro";
|
||||
import { Icon } from 'astro-icon/components'
|
||||
|
||||
export interface Props {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
prevUrl: string;
|
||||
nextUrl: string;
|
||||
}
|
||||
|
||||
const { currentPage, totalPages, prevUrl, nextUrl } = Astro.props;
|
||||
|
||||
const prev = currentPage > 1 ? "" : "disabled";
|
||||
const next = currentPage < totalPages ? "" : "disabled";
|
||||
---
|
||||
|
||||
{
|
||||
totalPages > 1 && (
|
||||
<nav class="pagination-wrapper" aria-label="Pagination">
|
||||
<LinkButton
|
||||
disabled={prev === "disabled"}
|
||||
href={prevUrl}
|
||||
className={`mr-4 select-none ${prev}`}
|
||||
ariaLabel="Previous"
|
||||
>
|
||||
<Icon name="fa6-solid:arrow-left" />
|
||||
Prev
|
||||
</LinkButton>
|
||||
{currentPage} / {totalPages}
|
||||
<LinkButton
|
||||
disabled={next === "disabled"}
|
||||
href={nextUrl}
|
||||
className={`ml-4 select-none ${next}`}
|
||||
ariaLabel="Next"
|
||||
>
|
||||
Next
|
||||
<Icon name="fa6-solid:arrow-right" />
|
||||
</LinkButton>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
<style>
|
||||
.pagination-wrapper {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.disabled {
|
||||
@apply pointer-events-none select-none opacity-50 hover:text-skin-base group-hover:fill-skin-base;
|
||||
}
|
||||
.disabled-svg {
|
||||
group-hover: !fill-skin-base;
|
||||
}
|
||||
</style>
|
|
@ -1,65 +0,0 @@
|
|||
---
|
||||
import LinkButton from "./LinkButton.astro";
|
||||
import { Icon } from "astro-icon/components";
|
||||
|
||||
|
||||
const URL = Astro.url;
|
||||
|
||||
const shareLinks = [
|
||||
{
|
||||
name: "WhatsApp",
|
||||
href: "https://wa.me/?text=",
|
||||
icon: "simple-icons:whatsapp",
|
||||
linkTitle: `Share this post via WhatsApp`,
|
||||
},
|
||||
{
|
||||
name: "Facebook",
|
||||
href: "https://www.facebook.com/sharer.php?u=",
|
||||
icon: "simple-icons:facebook",
|
||||
linkTitle: `Share this post on Facebook`,
|
||||
},
|
||||
{
|
||||
name: "Twitter/X",
|
||||
href: "https://twitter.com/intent/tweet?url=",
|
||||
icon: "simple-icons:x",
|
||||
linkTitle: `Tweet this post`,
|
||||
},
|
||||
{
|
||||
name: "Telegram",
|
||||
href: "https://t.me/share/url?url=",
|
||||
icon: "simple-icons:telegram",
|
||||
linkTitle: `Share this post via Telegram`,
|
||||
},
|
||||
{
|
||||
name: "Pinterest",
|
||||
href: "https://pinterest.com/pin/create/button/?url=",
|
||||
icon: "simple-icons:pinterest",
|
||||
linkTitle: `Share this post on Pinterest`,
|
||||
},
|
||||
{
|
||||
name: "Mail",
|
||||
href: "mailto:?subject=See%20this%20post&body=",
|
||||
icon: "fa6-solid:envelope",
|
||||
linkTitle: `Share this post via email`,
|
||||
},
|
||||
] as const;
|
||||
---
|
||||
|
||||
<div class={`social-icons`}>
|
||||
<span class="italic">Share this post on:</span>
|
||||
<div class="text-center">
|
||||
{
|
||||
shareLinks.map(social => (
|
||||
<LinkButton
|
||||
href={`${social.href + URL}`}
|
||||
className="link-button"
|
||||
title={social.linkTitle}
|
||||
>
|
||||
<Icon name={ social.icon } title={social.linkTitle} />
|
||||
</LinkButton>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
---
|
||||
import { SOCIALS } from "@config";
|
||||
import { Icon } from "astro-icon/components";
|
||||
---
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
|
||||
{
|
||||
SOCIALS.map(social => (
|
||||
<td>
|
||||
<a href={social.href} target="_blank" rel="noopener noreferrer">
|
||||
<Icon name={social.icon} title={social.name} class="icon has-text-white" />
|
||||
</a>
|
||||
</td>
|
||||
))
|
||||
}
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<style>
|
||||
|
||||
table { width: 100% }
|
||||
|
||||
.icon {
|
||||
|
||||
max-width: 55px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
flex-shrink: 1;
|
||||
|
||||
padding: 0 6px 0 6px;
|
||||
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
|
||||
}
|
||||
|
||||
</style>
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
export interface Props {
|
||||
tag: string;
|
||||
}
|
||||
|
||||
const { tag } = Astro.props;
|
||||
---
|
||||
|
||||
|
||||
<a
|
||||
href={`/tags/${tag}/`}
|
||||
transition:name={tag}
|
||||
>
|
||||
#<span>{tag}</span>
|
||||
</a>
|
||||
|
||||
<style>
|
||||
a {
|
||||
color: grey;
|
||||
}
|
||||
a:hover {
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
|
@ -1,51 +0,0 @@
|
|||
import type { GiteaProfile, Site, SocialObjects } from "./types";
|
||||
|
||||
export const SITE: Site = {
|
||||
website: "https://massive.box/", // replace this with your deployed domain
|
||||
author: "MassiveBox",
|
||||
desc: "MassiveBox is a free time developer and FOSS enthusiast. This is his website.",
|
||||
title: "MassiveBox",
|
||||
ogImage: "/og.webp",
|
||||
lightAndDarkMode: true,
|
||||
postPerPage: 3,
|
||||
scheduledPostMargin: 15 * 60 * 1000, // 15 minutes
|
||||
issoLocation: "https://isso.massive.box/", // empty to disable comments
|
||||
};
|
||||
|
||||
export const LOCALE = {
|
||||
lang: "en", // html lang code. Set this empty and default will be "en"
|
||||
langTag: ["en-EN"], // BCP 47 Language Tags. Set this empty [] to use the environment default
|
||||
} as const;
|
||||
|
||||
export const SOCIALS: SocialObjects = [
|
||||
{
|
||||
icon: "fa6-solid:envelope",
|
||||
href: "/email",
|
||||
name: "E-Mail"
|
||||
},
|
||||
{
|
||||
icon: "simple-icons:matrix",
|
||||
href: "https://matrix.to/#/@massivebox:massivebox.net",
|
||||
name: "Matrix",
|
||||
},
|
||||
{
|
||||
icon: "simple-icons:telegram",
|
||||
href: "https://t.me/massivebox",
|
||||
name: "Telegram",
|
||||
},
|
||||
{
|
||||
icon: "simple-icons:forgejo",
|
||||
href: "https://git.massive.box/massivebox",
|
||||
name: "Forgejo (Git hosting)",
|
||||
},
|
||||
{
|
||||
icon: "simple-icons:bluesky",
|
||||
href: "https://bsky.app/profile/massive.box",
|
||||
name: "Bluesky",
|
||||
},
|
||||
{
|
||||
icon: "fa6-solid:key",
|
||||
href: "https://keyoxide.org/box@massive.box",
|
||||
name: "Keyoxide",
|
||||
}
|
||||
];
|
|
@ -1,21 +0,0 @@
|
|||
import { SITE } from "@config";
|
||||
import { defineCollection, z } from "astro:content";
|
||||
|
||||
const blog = defineCollection({
|
||||
type: "content",
|
||||
schema: ({ image }) =>
|
||||
z.object({
|
||||
author: z.string().default(SITE.author),
|
||||
pubDatetime: z.date(),
|
||||
modDatetime: z.date().optional().nullable(),
|
||||
title: z.string(),
|
||||
featured: z.boolean().optional(),
|
||||
draft: z.boolean().optional(),
|
||||
tags: z.array(z.string()).default(["others"]),
|
||||
ogImage: image().optional(),
|
||||
description: z.string(),
|
||||
canonicalURL: z.string().optional(),
|
||||
}),
|
||||
});
|
||||
|
||||
export const collections = { blog };
|
2
src/env.d.ts
vendored
|
@ -1,2 +0,0 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
|
@ -1,130 +0,0 @@
|
|||
---
|
||||
import { LOCALE, SITE } from "@config";
|
||||
import "../../custom.scss";
|
||||
import Matomo from "../components/Matomo.astro";
|
||||
|
||||
const googleSiteVerification = import.meta.env.PUBLIC_GOOGLE_SITE_VERIFICATION;
|
||||
|
||||
export interface Props {
|
||||
pageTitle?: string;
|
||||
title?: string;
|
||||
author?: string;
|
||||
description?: string;
|
||||
ogImage?: string;
|
||||
canonicalURL?: string;
|
||||
pubDatetime?: Date;
|
||||
modDatetime?: Date | null;
|
||||
}
|
||||
|
||||
const {
|
||||
pageTitle = "Home",
|
||||
title = pageTitle + " - " +SITE.title,
|
||||
author = SITE.author,
|
||||
description = SITE.desc,
|
||||
ogImage = SITE.ogImage,
|
||||
canonicalURL = new URL(Astro.url.pathname, Astro.site).href,
|
||||
pubDatetime,
|
||||
modDatetime,
|
||||
} = Astro.props;
|
||||
|
||||
const socialImageURL = new URL(
|
||||
ogImage ?? SITE.ogImage ?? "og.png",
|
||||
Astro.url.origin
|
||||
).href;
|
||||
---
|
||||
|
||||
<!doctype html>
|
||||
<html
|
||||
lang=`${LOCALE.lang ?? "en"}`
|
||||
>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
|
||||
<!-- General Meta Tags -->
|
||||
<title>{title}</title>
|
||||
<meta name="title" content={title} />
|
||||
<meta name="description" content={description} />
|
||||
<meta name="author" content={author} />
|
||||
<link rel="sitemap" href="/sitemap-index.xml" />
|
||||
|
||||
<!-- Open Graph / Facebook -->
|
||||
<meta property="og:title" content={title} />
|
||||
<meta property="og:description" content={description} />
|
||||
<meta property="og:url" content={canonicalURL} />
|
||||
<meta property="og:image" content={socialImageURL} />
|
||||
|
||||
<!-- Article Published/Modified time -->
|
||||
{
|
||||
pubDatetime && (
|
||||
<meta
|
||||
property="article:published_time"
|
||||
content={pubDatetime.toISOString()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
modDatetime && (
|
||||
<meta
|
||||
property="article:modified_time"
|
||||
content={modDatetime.toISOString()}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
<!-- Twitter -->
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:url" content={canonicalURL} />
|
||||
<meta property="twitter:title" content={title} />
|
||||
<meta property="twitter:description" content={description} />
|
||||
<meta property="twitter:image" content={socialImageURL} />
|
||||
|
||||
<!-- Icons -->
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<meta name="msapplication-TileColor" content="#050732">
|
||||
<meta name="theme-color" content="#050732">
|
||||
|
||||
{
|
||||
// If PUBLIC_GOOGLE_SITE_VERIFICATION is set in the environment variable,
|
||||
// include google-site-verification tag in the heading
|
||||
// Learn more: https://support.google.com/webmasters/answer/9008080#meta_tag_verification&zippy=%2Chtml-tag
|
||||
googleSiteVerification && (
|
||||
<meta
|
||||
name="google-site-verification"
|
||||
content={googleSiteVerification}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
<!-- ViewTransitions /-->
|
||||
|
||||
<script is:inline src="/toggle-theme.js"></script>
|
||||
|
||||
<Matomo />
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<style>
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
html, body { max-width: 100%; overflow-x: hidden; }
|
||||
</style>
|
|
@ -1,40 +0,0 @@
|
|||
---
|
||||
import { Heading } from "react-bulma-components"
|
||||
|
||||
interface StringTitleProp {
|
||||
pageTitle?: string;
|
||||
pageDesc?: string;
|
||||
fullWidth?: boolean;
|
||||
}
|
||||
|
||||
interface ArrayTitleProp {
|
||||
pageTitle?: [string, string];
|
||||
titleTransition: string;
|
||||
pageDesc?: string;
|
||||
fullWidth?: boolean;
|
||||
}
|
||||
|
||||
export type Props = StringTitleProp | ArrayTitleProp;
|
||||
|
||||
const { props } = Astro;
|
||||
let maxWidth = props.fullWidth ? "84rem" : "" ;
|
||||
|
||||
---
|
||||
|
||||
<main id="main-content" style={{ maxWidth: maxWidth }}>
|
||||
{
|
||||
"titleTransition" in props ? (
|
||||
<Heading>
|
||||
{ props.pageTitle?.[0] }
|
||||
<span transition:name={props.titleTransition}>
|
||||
{ props.pageTitle?.[1] }
|
||||
</span>
|
||||
</Heading>
|
||||
) : (
|
||||
<Heading>{ props.pageTitle }</Heading>
|
||||
)
|
||||
}
|
||||
<p class="my-2">{ props.pageDesc }</p>
|
||||
<slot />
|
||||
</main>
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
import { SITE } from "@config";
|
||||
import Breadcrumbs from "@components/Breadcrumbs.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Layout from "./Layout.astro";
|
||||
|
||||
export interface Props {
|
||||
frontmatter: {
|
||||
title: string;
|
||||
description?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const { frontmatter } = Astro.props;
|
||||
---
|
||||
|
||||
<Layout title={`${frontmatter.title} | ${SITE.title}`}>
|
||||
<Header activeNav="about" />
|
||||
<main id="main-content">
|
||||
<section id="about" class="prose mb-28 max-w-3xl prose-img:border-0">
|
||||
<h1 class="title mb-3">{frontmatter.title}</h1>
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
|
@ -1,129 +0,0 @@
|
|||
---
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Tag from "@components/Tag.astro";
|
||||
import Datetime from "@components/Datetime.astro";
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import { slugifyStr } from "@utils/slugify";
|
||||
import ShareLinks from "@components/ShareLinks.astro";
|
||||
import { SITE } from "@config";
|
||||
import { Icon } from 'astro-icon/components'
|
||||
import { Level, Button } from "react-bulma-components"
|
||||
|
||||
export interface Props {
|
||||
post: CollectionEntry<"blog">;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
|
||||
const {
|
||||
title,
|
||||
author,
|
||||
description,
|
||||
ogImage,
|
||||
canonicalURL,
|
||||
pubDatetime,
|
||||
modDatetime,
|
||||
tags,
|
||||
} = post.data;
|
||||
|
||||
const { Content } = await post.render();
|
||||
|
||||
const ogImageUrl = typeof ogImage === "string" ? ogImage : ogImage?.src;
|
||||
const ogUrl = new URL(
|
||||
ogImageUrl ?? `/assets/og.webp`,
|
||||
Astro.url.origin
|
||||
).href;
|
||||
|
||||
const layoutProps = {
|
||||
title: `${title} | ${SITE.title}`,
|
||||
author,
|
||||
description,
|
||||
pubDatetime,
|
||||
modDatetime,
|
||||
canonicalURL,
|
||||
ogImage: ogUrl,
|
||||
scrollSmooth: true,
|
||||
};
|
||||
---
|
||||
|
||||
<Layout {...layoutProps}>
|
||||
<Header />
|
||||
|
||||
<main id="main-content">
|
||||
|
||||
<h1 transition:name={slugifyStr(title)} class="title">{title}</h1>
|
||||
|
||||
|
||||
<Datetime
|
||||
pubDatetime={pubDatetime}
|
||||
modDatetime={modDatetime}
|
||||
className="my-1"
|
||||
/>
|
||||
|
||||
{
|
||||
ogImage && <img src={ogImageUrl} alt="Cover image">
|
||||
}
|
||||
|
||||
<ul class="mb-4">
|
||||
{
|
||||
tags.map(tag => <Tag tag={slugifyStr(tag)} />)
|
||||
}
|
||||
</ul>
|
||||
|
||||
<article id="article" role="article">
|
||||
<div class="content">
|
||||
<Content />
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<Level className="my-4" breakpoint="mobile">
|
||||
<Level.Side>
|
||||
<Button id="back-to-top">
|
||||
<Icon name="fa6-solid:arrow-up" />
|
||||
<span class="ml-2">Back to Top</span>
|
||||
</Button>
|
||||
</Level.Side>
|
||||
<Level.Side align="right">
|
||||
<ShareLinks />
|
||||
</Level.Side>
|
||||
</Level>
|
||||
|
||||
{
|
||||
SITE.issoLocation != "" && (
|
||||
<p><span class="is-size-3"><b>Comments</b></span> (<a target="_blank" rel="noopener noreferrer" href="/pages/blog-legal#comments-rules">rules</a>, <a target="_blank" rel="noopener noreferrer" href="/pages/blog-legal#comments-privacy">privacy</a>)</p>
|
||||
<script is:inline data-isso={ SITE.issoLocation } src={ SITE.issoLocation + "/js/embed.min.js" } async></script>
|
||||
<div id="isso-thread"><noscript>You need to enable JavaScript to see and post comments.</noscript></div>
|
||||
)
|
||||
}
|
||||
|
||||
</main>
|
||||
<Footer />
|
||||
</Layout>
|
||||
|
||||
<script is:inline>
|
||||
|
||||
function addHeadingLinks() {
|
||||
let headings = Array.from(document.querySelectorAll("h2, h3, h4, h5, h6"));
|
||||
for (let heading of headings) {
|
||||
heading.classList.add("group");
|
||||
let link = document.createElement("a");
|
||||
link.innerText = "#";
|
||||
link.className = "ml-2";
|
||||
link.href = "#" + heading.id;
|
||||
link.ariaHidden = "true";
|
||||
heading.appendChild(link);
|
||||
}
|
||||
}
|
||||
addHeadingLinks();
|
||||
|
||||
function backToTop() {
|
||||
document.querySelector("#back-to-top")?.addEventListener("click", () => {
|
||||
document.body.scrollTop = 0; // For Safari
|
||||
document.documentElement.scrollTop = 0; // For Chrome, Firefox, IE and Opera
|
||||
});
|
||||
}
|
||||
backToTop();
|
||||
|
||||
</script>
|
|
@ -1,47 +0,0 @@
|
|||
---
|
||||
import type { CollectionEntry } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import Pagination from "@components/Pagination.astro";
|
||||
import BlogCard from "../components/BlogCard.astro";
|
||||
import { Button } from "react-bulma-components";
|
||||
import { Icon } from "astro-icon/components";
|
||||
|
||||
export interface Props {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
paginatedPosts: CollectionEntry<"blog">[];
|
||||
}
|
||||
|
||||
const { currentPage, totalPages, paginatedPosts} = Astro.props;
|
||||
---
|
||||
|
||||
<Layout pageTitle="Blog">
|
||||
<Header activeNav="blog" />
|
||||
<Main pageTitle="Blog" pageDesc="All the articles I've posted.">
|
||||
<a href="/rss.xml">
|
||||
<Button className="mb-5">
|
||||
<Icon name="fa6-solid:rss" />
|
||||
<span class="ml-2">RSS Feed</span>
|
||||
</Button>
|
||||
</a>
|
||||
<div>
|
||||
{
|
||||
paginatedPosts.map(({ data, slug }) => (
|
||||
<BlogCard href={`/blog/${slug}/`} frontmatter={data} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</Main>
|
||||
|
||||
<Pagination
|
||||
{currentPage}
|
||||
{totalPages}
|
||||
prevUrl={`/blog${currentPage - 1 !== 1 ? "/" + (currentPage - 1) : ""}/`}
|
||||
nextUrl={`/blog/${currentPage + 1}/`}
|
||||
/>
|
||||
|
||||
<Footer />
|
||||
</Layout>
|
|
@ -1,49 +0,0 @@
|
|||
---
|
||||
import { type CollectionEntry } from "astro:content";
|
||||
import Layout from "@layouts/Layout.astro";
|
||||
import Main from "@layouts/Main.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Footer from "@components/Footer.astro";
|
||||
import BlogCard from "../components/BlogCard.astro";
|
||||
import Pagination from "@components/Pagination.astro";
|
||||
import { SITE } from "@config";
|
||||
|
||||
export interface Props {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
paginatedPosts: CollectionEntry<"blog">[];
|
||||
tag: string;
|
||||
tagName: string;
|
||||
}
|
||||
|
||||
const { currentPage, totalPages, paginatedPosts, tag, tagName } = Astro.props;
|
||||
---
|
||||
|
||||
<Layout title={`Tag: ${tagName} | ${SITE.title}`}>
|
||||
<Header activeNav="tags" />
|
||||
<Main
|
||||
pageTitle={[`Tag:`, `${tagName}`]}
|
||||
titleTransition={tag}
|
||||
pageDesc={`All the articles with the tag "${tagName}".`}
|
||||
>
|
||||
<h1 slot="title" transition:name={tag}>{`Tag:${tag}`}</h1>
|
||||
<ul>
|
||||
{
|
||||
paginatedPosts.map(({ data, slug }) => (
|
||||
<BlogCard href={`/posts/${slug}/`} frontmatter={data} />
|
||||
))
|
||||
}
|
||||
</ul>
|
||||
</Main>
|
||||
|
||||
<Pagination
|
||||
{currentPage}
|
||||
{totalPages}
|
||||
prevUrl={`/tags/${tag}${
|
||||
currentPage - 1 !== 1 ? "/" + (currentPage - 1) : ""
|
||||
}/`}
|
||||
nextUrl={`/tags/${tag}/${currentPage + 1}/`}
|
||||
/>
|
||||
|
||||
<Footer noMarginTop={totalPages > 1} />
|
||||
</Layout>
|