Compare commits

..

No commits in common. "main" and "legacy" have entirely different histories.
main ... legacy

132 changed files with 698 additions and 17921 deletions

View file

@ -1,6 +0,0 @@
.husky
.vscode
node_modules
public
dist
.yarn

View file

@ -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
View file

@ -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
View 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

View file

@ -1,4 +0,0 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npx lint-staged

5
.idea/.gitignore generated vendored
View file

@ -1,5 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

View file

@ -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">{
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="2gyJYMuj2FYRcYSjxshnxqqMAxs" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;JavaScript Debug.localhost:4321.executor&quot;: &quot;Run&quot;,
&quot;Node.js.Unnamed.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/massive/Dev/website/src/pages/contact&quot;,
&quot;list.type.of.created.stylesheet&quot;: &quot;CSS&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;npm.build.executor&quot;: &quot;Run&quot;,
&quot;npm.dev --host.executor&quot;: &quot;Run&quot;,
&quot;npm.dev.executor&quot;: &quot;Run&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;watcher.settings&quot;,
&quot;ts.external.directory.path&quot;: &quot;/home/massive/Dev/website/node_modules/typescript/lib&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</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>

View file

@ -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">{
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="2gyJYMuj2FYRcYSjxshnxqqMAxs" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;JavaScript Debug.localhost:4321.executor&quot;: &quot;Run&quot;,
&quot;Node.js.Unnamed.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/massive/Dev/website/src/pages/contact&quot;,
&quot;list.type.of.created.stylesheet&quot;: &quot;CSS&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;npm.build.executor&quot;: &quot;Run&quot;,
&quot;npm.dev --host.executor&quot;: &quot;Run&quot;,
&quot;npm.dev.executor&quot;: &quot;Run&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;watcher.settings&quot;,
&quot;ts.external.directory.path&quot;: &quot;/home/massive/Dev/website/node_modules/typescript/lib&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</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>

View file

@ -1,58 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<option name="LINE_SEPARATOR" value="&#10;" />
<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>

View file

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

View file

@ -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>

View file

@ -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
View file

@ -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
View file

@ -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>

View file

@ -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
View file

@ -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>

View file

@ -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">{
&quot;associatedIndex&quot;: 8
}</component>
<component name="ProjectId" id="2gyJYMuj2FYRcYSjxshnxqqMAxs" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;JavaScript Debug.localhost:4321.executor&quot;: &quot;Run&quot;,
&quot;Node.js.Unnamed.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;/home/massive/Dev/website/src/content/blog&quot;,
&quot;list.type.of.created.stylesheet&quot;: &quot;CSS&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;npm.build.executor&quot;: &quot;Run&quot;,
&quot;npm.dev --host.executor&quot;: &quot;Run&quot;,
&quot;npm.dev.executor&quot;: &quot;Run&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;watcher.settings&quot;,
&quot;ts.external.directory.path&quot;: &quot;/home/massive/Dev/website/node_modules/typescript/lib&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
}
}</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>

View file

@ -1,4 +0,0 @@
{
"MD033": false,
"MD013": false
}

2
.npmrc
View file

@ -1,2 +0,0 @@
# Expose Astro dependencies for `pnpm` users
shamefully-hoist=true

View file

@ -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

View file

@ -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"
}
}
]
}

169
LICENSE
View file

@ -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
<https://www.gnu.org/licenses/>.
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>.

View file

@ -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:
```
![ogImage](:/0f409e2b6faf44e48c5c0d99453d02c0)
```
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
assets/homeicons/c.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
assets/homeicons/docker.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

BIN
assets/homeicons/front.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
assets/homeicons/go.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
assets/homeicons/linux.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
assets/homeicons/mongo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
assets/homeicons/php.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
assets/homeicons/py.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
assets/homeicons/redis.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

BIN
assets/homeicons/sql.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
assets/maurizio.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
assets/navlogo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
assets/projects/2048.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
assets/projects/jiljil.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
assets/projects/palla.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
assets/projects/sogs.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

BIN
assets/spinning.mp4 Normal file

Binary file not shown.

BIN
assets/spinning.webm Normal file

Binary file not shown.

View 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
View 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>

View file

@ -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

View file

@ -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
View 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

File diff suppressed because it is too large Load diff

View file

@ -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
View 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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

View file

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

View file

@ -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

View file

@ -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"
}

View file

@ -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();
});

View file

@ -1 +0,0 @@
declare module 'remark-collapse';

View file

@ -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>

View file

@ -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">&raquo;</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">&raquo;</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>

View file

@ -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>

View file

@ -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">
&copy; { 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>

View file

@ -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>

View file

@ -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>

View file

@ -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>
)
}

View file

@ -1 +0,0 @@
<script defer src="https://stats.massive.box/p.js" data-website-id="30344937-ffdf-4a6a-ba52-dc60a23aa4f2"></script>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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",
}
];

View file

@ -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
View file

@ -1,2 +0,0 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="astro/client" />

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

Some files were not shown because too many files have changed in this diff Show more