Created blog page, many improvements
This commit is contained in:
parent
cfbaf56159
commit
3853e8fa20
|
@ -1 +1 @@
|
|||
[{"D:\\Software\\Development\\Websites\\enderman.ch\\index\\assets\\styles\\transitions.scss":"1","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\TransitionY.vue":"2","D:\\Software\\Development\\Websites\\enderman.ch\\index\\app.vue":"3","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\EMail.vue":"4","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\about.vue":"5","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\index.vue":"6","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\projects.vue":"7","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\social.vue":"8","D:\\Software\\Development\\Websites\\enderman.ch\\index\\layouts\\Card.vue":"9","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Options.vue":"10","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Flooter.vue":"11","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\SwipeControls.vue":"12","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Logo.vue":"13","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Route.vue":"14","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\animations\\Portal.vue":"15","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\ui\\Icon.vue":"16"},{"size":165,"mtime":1700688268778,"hashOfConfig":"17"},{"size":1513,"mtime":1700430002840,"hashOfConfig":"17"},{"size":1550,"mtime":1700850776284,"hashOfConfig":"17"},{"size":804,"mtime":1700688952880,"hashOfConfig":"17"},{"size":2135,"mtime":1700689265787,"hashOfConfig":"17"},{"size":3117,"mtime":1700689271383,"hashOfConfig":"17"},{"size":2304,"mtime":1700689265769,"hashOfConfig":"17"},{"size":3232,"mtime":1700689265781,"hashOfConfig":"17"},{"size":1623,"mtime":1700748530633,"hashOfConfig":"17"},{"size":2097,"mtime":1706459320334,"hashOfConfig":"17"},{"size":2045,"mtime":1706266444695,"hashOfConfig":"17"},{"size":1236,"mtime":1706205683132,"hashOfConfig":"17"},{"size":1135,"mtime":1706030243608,"hashOfConfig":"17"},{"size":979,"mtime":1706003786966,"hashOfConfig":"17"},{"size":16911,"mtime":1706027251875,"hashOfConfig":"17"},{"size":935,"mtime":1706445231843,"hashOfConfig":"17"},"5tgxr3"]
|
||||
[{"D:\\Software\\Development\\Websites\\enderman.ch\\index\\assets\\styles\\transitions.scss":"1","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\TransitionY.vue":"2","D:\\Software\\Development\\Websites\\enderman.ch\\index\\app.vue":"3","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\EMail.vue":"4","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\about.vue":"5","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\index.vue":"6","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\projects.vue":"7","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\social.vue":"8","D:\\Software\\Development\\Websites\\enderman.ch\\index\\layouts\\Card.vue":"9","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Options.vue":"10","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Flooter.vue":"11","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\SwipeControls.vue":"12","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Logo.vue":"13","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\blocks\\Route.vue":"14","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\animations\\Portal.vue":"15","D:\\Software\\Development\\Websites\\enderman.ch\\index\\components\\ui\\Icon.vue":"16","D:\\Software\\Development\\Websites\\enderman.ch\\index\\assets\\styles\\vuetify.scss":"17","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\[...slug].vue":"18","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\blog\\index.vue":"19","D:\\Software\\Development\\Websites\\enderman.ch\\index\\pages\\blog\\[slug].vue":"20"},{"size":165,"mtime":1700688268778,"hashOfConfig":"21"},{"size":1513,"mtime":1700430002840,"hashOfConfig":"21"},{"size":1550,"mtime":1700850776284,"hashOfConfig":"21"},{"size":804,"mtime":1700688952880,"hashOfConfig":"21"},{"size":2135,"mtime":1700689265787,"hashOfConfig":"21"},{"size":3117,"mtime":1700689271383,"hashOfConfig":"21"},{"size":2304,"mtime":1700689265769,"hashOfConfig":"21"},{"size":3232,"mtime":1700689265781,"hashOfConfig":"21"},{"size":1623,"mtime":1700748530633,"hashOfConfig":"21"},{"size":2187,"mtime":1706788559308,"hashOfConfig":"22"},{"size":2067,"mtime":1708180203174,"hashOfConfig":"22"},{"size":1236,"mtime":1706205683132,"hashOfConfig":"22"},{"size":1135,"mtime":1706030243608,"hashOfConfig":"22"},{"size":979,"mtime":1706003786966,"hashOfConfig":"22"},{"size":16968,"mtime":1708180277353,"hashOfConfig":"22"},{"size":935,"mtime":1706445231843,"hashOfConfig":"22"},{"size":120,"mtime":1706789613657,"hashOfConfig":"23"},{"size":1988,"mtime":1708191547889,"hashOfConfig":"22"},{"size":2560,"mtime":1708202402381,"hashOfConfig":"22"},{"size":983,"mtime":1708206997656,"hashOfConfig":"22"},"5tgxr3","1nljphs","1lk8nat"]
|
2
app.vue
2
app.vue
|
@ -2,8 +2,6 @@
|
|||
const config = useAppConfig()
|
||||
const theme = useVThemeSSR()
|
||||
|
||||
console.log('theme.dark.value: ', theme.dark.value)
|
||||
|
||||
useHead({
|
||||
titleTemplate: (chunk?) => {
|
||||
return chunk === config.title.short
|
||||
|
|
|
@ -20,6 +20,10 @@
|
|||
--animate-duration: 1s;
|
||||
--animate-delay: 0.75s;
|
||||
--animate-repeat: 1;
|
||||
--bs-font-sans-serif: 'Lato', 'Times New Roman';
|
||||
--bs-font-monospace: 'JetBrains Mono', 'Courier New';
|
||||
--bs-body-font-size: 18px;
|
||||
--bs-body-line-height: 1.5;
|
||||
}
|
||||
|
||||
// The HTML page.
|
||||
|
@ -47,9 +51,6 @@ body {
|
|||
|
||||
color: white;
|
||||
|
||||
font-family: Lato, sans-serif;
|
||||
font-size: 18px;
|
||||
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
|
@ -106,6 +107,11 @@ body {
|
|||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pre-wrap {
|
||||
white-space: pre-wrap;
|
||||
word-break: keep-all;
|
||||
}
|
||||
|
||||
// Query-overridable classes.
|
||||
.background {
|
||||
background-color: rgb(0 0 0 / 50%);
|
||||
|
@ -271,6 +277,73 @@ body {
|
|||
margin: auto;
|
||||
}
|
||||
|
||||
.post {
|
||||
&-box {
|
||||
position: relative;
|
||||
|
||||
padding: 0.6942rem 0.6942rem 3rem;
|
||||
|
||||
border: 2px solid rgb(153 153 255 / 60%);
|
||||
background-color: rgb(45 7 110 / 10%);
|
||||
|
||||
transition: 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: rgb(153 153 255 / 100%);
|
||||
background-color: rgb(45 7 110 / 30%);
|
||||
}
|
||||
}
|
||||
|
||||
&-pocket {
|
||||
position: absolute;
|
||||
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
|
||||
padding: 0.5rem;
|
||||
|
||||
text-transform: uppercase;
|
||||
background-color: rgb(153 153 255 / 10%);
|
||||
|
||||
border-left: 1px solid rgb(153 153 255 / 60%);
|
||||
border-top: 1px solid rgb(153 153 255 / 60%);
|
||||
|
||||
border-top-left-radius: var(--bs-border-radius-xl);
|
||||
border-bottom-right-radius: var(--bs-border-radius-xl);
|
||||
}
|
||||
|
||||
&-thumb {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
height: 100%;
|
||||
|
||||
> img {
|
||||
width: 100%;
|
||||
height: 280px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&-preamble {
|
||||
&-thumb {
|
||||
max-width: 800px;
|
||||
}
|
||||
}
|
||||
|
||||
&-content {
|
||||
a {
|
||||
@extend %link;
|
||||
|
||||
|
||||
padding-left: 0.5rem;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic classes.
|
||||
@include bootstrap.media-breakpoint-up(sm) {
|
||||
#ender-app {
|
||||
|
@ -282,7 +355,7 @@ body {
|
|||
}
|
||||
|
||||
.dimensions {
|
||||
width: 80%;
|
||||
width: 90%;
|
||||
min-height: fit-content;
|
||||
max-height: 90%;
|
||||
}
|
||||
|
@ -310,23 +383,56 @@ body {
|
|||
}
|
||||
|
||||
@include bootstrap.media-breakpoint-up(md) {
|
||||
/* TODO: Fill it up :) */
|
||||
.post {
|
||||
&-box {
|
||||
padding-bottom: 0.6942rem;
|
||||
}
|
||||
|
||||
&-thumb {
|
||||
min-width: 352px;
|
||||
|
||||
> img {
|
||||
max-width: 352px;
|
||||
height: 198px;
|
||||
}
|
||||
}
|
||||
|
||||
&-title {
|
||||
display: -webkit-box;
|
||||
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-description {
|
||||
display: -webkit-box;
|
||||
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include bootstrap.media-breakpoint-up(lg) {
|
||||
.dimensions {
|
||||
width: 70%;
|
||||
width: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
@include bootstrap.media-breakpoint-up(xl) {
|
||||
.dimensions {
|
||||
width: 40%;
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
|
||||
@include bootstrap.media-breakpoint-up(xxl) {
|
||||
.dimensions {
|
||||
width: 30%;
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
@use 'vuetify/settings' with (
|
||||
$utilities: false,
|
||||
$color-pack: false,
|
||||
|
||||
$button-banner-actions-padding: 0 69px,
|
||||
);
|
|
@ -2,6 +2,9 @@
|
|||
import sky from '~/assets/images/textures/sky.png'
|
||||
import particles from '~/assets/images/textures/particles.png'
|
||||
|
||||
const config = useAppConfig()
|
||||
const fqdn = config.url.split('//')[1]
|
||||
|
||||
const resources: string[] = [sky, particles]
|
||||
|
||||
const props = defineProps({
|
||||
|
@ -316,7 +319,7 @@ class Portal {
|
|||
|
||||
notice() {
|
||||
console.log(
|
||||
`%c \u2587%c\u2587%c\u2587 %c\u2587%c\u2587%c\u2587 %c // ECMAPortal v${this.version} by Endermanch & WiPet\n\n\thttps://enderman.ch\n\thttps://go.enderman.ch/wipet`,
|
||||
`%c \u2587%c\u2587%c\u2587 %c\u2587%c\u2587%c\u2587 %c // ECMAPortal v${this.version} by Endermanch & WiPet\n\n\t${config.url}\n\t${config.shortener}/wipet`,
|
||||
'color: #E58EFF',
|
||||
'color: #D52DFF',
|
||||
'color: #E58EFF',
|
||||
|
@ -327,7 +330,7 @@ class Portal {
|
|||
)
|
||||
|
||||
console.log(
|
||||
'If any errors occur below, please send a screenshot of them my way!\n\t%ccontact@enderman.ch',
|
||||
`If any errors occur below, please send a screenshot of them my way!\n\t%ccontact@${fqdn}`,
|
||||
'color: #87CEFA',
|
||||
)
|
||||
}
|
||||
|
|
|
@ -4,9 +4,10 @@ const props = defineProps({
|
|||
})
|
||||
|
||||
const config = useAppConfig()
|
||||
const fqdn = config.url.split('//')[1]
|
||||
|
||||
const currentYear = new Date().getFullYear()
|
||||
const mailTemplate = `I've just found a bug on https://enderman.ch and would like to report it.%0D%0A%0D%0AWebsite version: ${
|
||||
const mailTemplate = `I've just found a bug on ${config.url} and would like to report it.%0D%0A%0D%0AWebsite version: ${
|
||||
config.build.version
|
||||
}%0D%0ABuild date: ${
|
||||
config.build.date
|
||||
|
@ -29,9 +30,9 @@ const mailTemplate = `I've just found a bug on https://enderman.ch and would lik
|
|||
'link-hi-force-dark': !props.opaque,
|
||||
'link-hi': props.opaque,
|
||||
}"
|
||||
address="contact@enderman.ch"
|
||||
cc="admin@enderman.ch"
|
||||
subject="Bug report: enderman.ch"
|
||||
:address="`contact@${fqdn}`"
|
||||
:cc="`admin@${fqdn}`"
|
||||
:subject="`Bug report: ${fqdn}`"
|
||||
:body="mailTemplate"
|
||||
>
|
||||
<strong>Report a bug</strong>
|
||||
|
@ -43,7 +44,7 @@ const mailTemplate = `I've just found a bug on https://enderman.ch and would lik
|
|||
'link-hi-force-dark': !props.opaque,
|
||||
'link-hi': props.opaque,
|
||||
}"
|
||||
href="https://enderman.ch"
|
||||
:href="config.url"
|
||||
>Enderman</a
|
||||
>. All rights reserved.
|
||||
<wbr />
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import cogIcon from '~/assets/images/icons/cog.png'
|
||||
import pearlIcon from '~/assets/images/icons/pearl.gif'
|
||||
|
||||
const theme = useVThemeSSR()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -46,13 +48,13 @@ import pearlIcon from '~/assets/images/icons/pearl.gif'
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Report a bug
|
||||
</VCardText>
|
||||
<VCardActions>
|
||||
<VSpacer></VSpacer>
|
||||
<VBtn color="danger" @click="theme.toggle()" />
|
||||
<VBtn
|
||||
color="success"
|
||||
variant="outlined"
|
||||
prepend-icon="close"
|
||||
@click="isActive.value = false"
|
||||
>
|
||||
|
|
|
@ -22,6 +22,7 @@ interface ThemeConfig {
|
|||
|
||||
type config = {
|
||||
url: string
|
||||
shortener: string
|
||||
name: string
|
||||
title: TitleConfig
|
||||
description: string
|
||||
|
@ -32,6 +33,7 @@ type config = {
|
|||
|
||||
export default {
|
||||
url: 'https://enderman.ch',
|
||||
shortener: 'https://go.enderman.ch',
|
||||
name: packageJSON.name || 'app',
|
||||
title: {
|
||||
full: "Enderman's Website",
|
||||
|
@ -47,7 +49,7 @@ export default {
|
|||
theme: {
|
||||
file: './assets/styles/vuetify.scss',
|
||||
cookie: 'color-scheme',
|
||||
default: 'vanilla',
|
||||
default: 'chocolate',
|
||||
light: 'vanilla',
|
||||
dark: 'chocolate',
|
||||
themes: {
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
title: Hello World!
|
||||
description: I created this post to test @nuxt/content.
|
||||
created: 2023-08-12T16:40:09Z
|
||||
updated: 2024-02-18T20:31:54Z
|
||||
draft: false
|
||||
tags: ['hello', 'world']
|
||||
thumbnail: '/images/blog/thumbnails/hello.jpg'
|
||||
---
|
||||
|
||||
# Hello!
|
||||
|
||||
This is an example piece of content
|
|
@ -0,0 +1,344 @@
|
|||
---
|
||||
title: The Windows Context Menu – Is It a Lost Cause?
|
||||
description: We dive in deep into the Windows context menu and how to customize it to your liking.
|
||||
created: 2023-08-13T15:51:19Z
|
||||
updated: 2024-02-18T21:32:12Z
|
||||
draft: false
|
||||
tags: ['windows', 'context-menu']
|
||||
thumbnail: '/images/blog/thumbnails/the-windows-context-menu.png'
|
||||
---
|
||||
|
||||
#### The Windows context menu is... poorly done.
|
||||
|
||||
And third-party software developers adding oil into the dumpster fire aren't helping with it. It's not their fault, though. There's no common standard and almost no documentation for adding entries into the context menu.
|
||||
|
||||
At this point, there's no return and no salvation for it. At very least it's working. What actually doesn't help is that Microsoft continued with their all time classic - piling up layers upon layers of indirection with their all new Windows 11 context menu, which is also poorly done and even more enclosed in terms of the API.
|
||||
|
||||
I want to dedicate this blog post to _actually customizing_ the Windows 10/11 Legacy context menu to your liking instead of simply complaining about its inner workings. Using methods described in this article, you will be able to bring your menu from this abomination...
|
||||
|
||||

|
||||
|
||||
**...to this!**
|
||||
|
||||

|
||||
|
||||
Be aware - there's _plenty_ of registry digging involved. And the truly worst part is...
|
||||
|
||||
**Forget any logic when dealing with registry keys.** The idea of registry's structural integrity has long since been abandoned and nobody seems to care about it as, well, the end user is never going to care about it, and if they do - they know what they're doing and they will not complain.
|
||||
While there are layers of abstraction applied to the most Windows settings, the context menu is, unfortunately, left aside. And much to our dismay, its backend is also janky, terrible and tangled up.
|
||||
|
||||
The only fair question to reader from me is "Do you really believe it's worth your time?". And if your answer is yes, and you're as much of a perfectionist as I am, buckle up and get ready for upcoming few hours of pure joy.
|
||||
|
||||
Please note that any sfc /scannow or dism /restorehealth invocations will probably interfere with your context menu as Microsoft are really weird - they seem to allow customization of it, but still don't fully support it. It's been like this for at least 20 years.
|
||||
|
||||
Shell entries & Context Menu handlers
|
||||
There are two main ways to insert entries into the context menu:
|
||||
|
||||
Shell entries
|
||||
Context Menu handlers
|
||||
Both of them are very poorly documented, but the idea is very simple: whereas Shell entries are managed by the registry, the Context Menu handlers are managed by the software in question.
|
||||
Shell entries are much, much better in terms of flexibility and customization (in fact, you don't get any customization with Context Menu handlers at all, unless the developer was nice enough to let you tweak context menu settings through their app or provide necessary documentation for registry endpoints). 7-Zip would serve as a neat example of good software design, as the creator allows you to tweak settings of their handler.
|
||||
|
||||

|
||||
|
||||
MobaXterm would also serve as a nice example for the case, where shell entries are used, but you can tweak them from the app itself.
|
||||
|
||||
MobaXterm Context Menu Settings.png
|
||||
|
||||
And so, there are 3 types of applications in terms of their context menu customization-friendliness:
|
||||
|
||||
Context Menu handler apps with in-app settings (the easiest)
|
||||
Apps using shell entries (customizable via registry hacks or from within software)
|
||||
Context Menu handler apps without anything (uneditable entries)
|
||||
The first case is the simplest - you just have to consult the documentation of software you're using to find out whether it supports context menu customization or not.
|
||||
|
||||
The other two cases is what I'm going to tackle in this write-up.
|
||||
|
||||
For starters, all modifications are done under HKEY_CLASSES_ROOT (the HKEY_LOCAL_MACHINE\Software\Classes alias). That hive contains data about shell components and file extensions. For example, any data under exefile will apply to all executable files. That's one of the methods companion viruses utilized back in the day to accompany their victims - the .exe/.com files. Either way, since the HKCR hive is basically divided into file/shell extensions, we will have to edit context menu for each file and shell extension manually. Yeah. This is absolutely abysmal. There is no better way to approach this. And while you might already be reconsidering your choices, let me teach you the basics so you could at least bring your most used extensions and most noticeable context menu areas up to par... Some generalizations are also made within the registry. For instance, there are wildcard entries for ALL files, or for ALL images and so on. That still does not excuse the horrible approach Microsoft has taken with the file extensions from the beginning.
|
||||
|
||||
Even though the incompleteness always annoyed me, unfortunately, that's how sometimes life is.
|
||||
|
||||
This is how the general structure of an extension looks like:
|
||||
EXE File Registry Structure.png
|
||||
|
||||
Editable entries are stored under the Shell key. Uneditable/highly customizable (thanks to the developer) entires are stored under ShellEx. Capitalization plays no role, the Windows registry key names is case-insensitive. The value names, however, are. What a nice spec!
|
||||
|
||||
Sort of editable entries?
|
||||
The structure varies from extension to extension, but you know you've found it when you see the Shell key. Subkeys of it represent the "shell entries" - or, well, customizable entries of the context menu.
|
||||
|
||||
Directory.png
|
||||
|
||||
Each shell entry contains the command subkey, default value of which controls what executes upon clicking on the context menu entry.
|
||||
The default entry follows the Batch syntax, albeit very poorly. You cannot have extended Batch logic in the entries, which made me immeasurably sad and also made me write utilities called quiet and elevate for that specific purpose. No matter how hard I tried, I could not find the spec for the extents of the Batch logic in the default value for command.
|
||||
|
||||
Shell Entry Structure.png
|
||||
Command Verb.png
|
||||
|
||||
In general, I'd suggest you to treat the command value as the raw command line, but with the %1 macro for the full input file path without quotation marks (e.g. if you right-click C:\Users\bleh\p.txt, and click on your own shell entry, the %1 within command will be equivalent to C:\Users\bleh\p.txt) and %V for the full directory path. That must be one of the few values to be consistent across all the shell entries.
|
||||
|
||||
Sometimes command's default value is left unused for DelegateExecute to take its place.
|
||||
If that's the case, that means the command handler is implemented as an IExecuteCommand COM object, or, well, you can't really customize it. All it contains is the UUID of the read-only object.
|
||||
|
||||
DelegateExecute.png
|
||||
|
||||
Adding entries
|
||||
Locate the extension (be it a file, or a shell one) you want to target.
|
||||
Careful! Some extensions can be buried within other extensions. For example, the folder background extension is located inside the folder extension under the Background key.
|
||||
Navigate to the shell key. If there is none and you're confident there should be one (by feel!), try creating one (it's highly likely not going to affect the system stability).
|
||||
Create a new entry, you can call it whatever you want, but it should be alphanumeric.
|
||||
Create a command key.
|
||||
Specify the command line you want to execute upon the click of your entry.
|
||||
The entry should appear in the context menu. If it does not, restart Explorer. If it still doesn't appear, you've probably messed up the procedure or the extension isn't registered in Windows.
|
||||
Nesting entries
|
||||
You can nest entries too! Repeat the first 4 steps from The Windows Context Menu > Adding entries, but don't add the command key yet. Instead, create another shell key, and then inside of it create a key not called command. And working in that new key, repeat last 3 steps. Finally, get back to your initial ancestor entry and add an empty REG_SZ value called SubCommands.
|
||||
|
||||
Nested Entries Structure.png
|
||||
SubCommands.png
|
||||
|
||||
You can make an entry have only a single nested entry as well, I've tested it.
|
||||
|
||||
Nested Entries.png
|
||||
|
||||
To control the parent context menu entry, use the outer Shell entry subkey.
|
||||
|
||||
Nested Entries Order.png
|
||||
|
||||
To control its children, use the inner Shell entry subkeys.
|
||||
|
||||
Nested Entries Order 2.png
|
||||
|
||||
Editing entries
|
||||
Now you're probably wondering how to name the entries and add nice little icons next to them.
|
||||
By default, the context menu entries duplicate their names from their respective shell subkeys.
|
||||
|
||||
Name
|
||||
To override the default name, you can use either the (Default) or MUIVerb REG_SZ values inside the respective shell entry (not command). MUIVerb takes precedence over the default value.
|
||||
|
||||
You can put in anything you want, it's going to work.
|
||||
|
||||
Icon
|
||||
To enable the icon for your shell entry, you should use the Icon REG_SZ value inside the respective shell entry. It uses that weird WinAPI resource syntax to create a direct link to the icon.
|
||||
|
||||
PATH_TO_FILE_WITH_ICON_RESOURCES,INDEX
|
||||
If you want to select the first icon (in that case, the INDEX part is 0), the INDEX part can be omitted completely, as in:
|
||||
|
||||
PATH_TO_FILE_WITH_ICON_RESOURCES
|
||||
For example, the following expressions are completely valid and are going to work:
|
||||
|
||||
"C:\Windows\system32\cmd.exe"
|
||||
C:\Windows\regedit.exe
|
||||
"C:\Windows\explorer.exe",0
|
||||
C:\Windows\system32\cmd.exe,0
|
||||
cmd.exe
|
||||
shell32.dll,3
|
||||
SHELL32.DLL,10
|
||||
"C:\Windows\system32\SHELL32.DLL",9
|
||||
You can omit the quotes and full paths depending on the %PATH% variable that unholy concoction of an API uses. Only God knows what paths it's able to parse by default. C:\Windows, C:\Windows\system32 work for sure, though.
|
||||
|
||||
I still highly suggest supplying the full path to file, as even the %PATH%s of the Explorer's icon picker and the Icon value do not seem to correspond.
|
||||
|
||||
Tip: you can use the Explorer folder icon picker to acquire the icon DLL and calculate the offset of the desired icon. For example, the one I've selected in the screenshot has an index of 5.
|
||||
|
||||
Indexing begins with 0 and ascends in columns.
|
||||
|
||||
Icon Viewer.png
|
||||
|
||||
You can also pull resource strings from files the same way for MUI Verbs and default values.
|
||||
|
||||
DLL Syntax.png
|
||||
|
||||
Separators
|
||||
Another integral part of the context menu, which is hardly documented by anyone. Managing them is also rather quirky, but you get used to it after some time.
|
||||
|
||||
Separators in shell entries are relative to the element you're enabling them for and can overlap without any issue.
|
||||
|
||||
Use SeparatorBefore and SeparatorAfter empty REG_SZ values to add a separator before or after the entry you're editing, respectively.
|
||||
|
||||
Separators.png
|
||||
|
||||
Appear in the Shift+Click context menu only
|
||||
You can make your shell entry be "hidden" from a normal context menu click. Such entry is called "extended". It's possible to enable that functionality by creating an empty REG_SZ value called Extended inside the respective shell entry.
|
||||
|
||||
Position override
|
||||
See The Windows Context Menu > Sorting entries > Position override
|
||||
|
||||
Hide in Safe Mode
|
||||
To hide the context menu entry in safe mode, create an empty REG_SZ value called HideInSafeMode inside the respective shell entry.
|
||||
|
||||
Never default
|
||||
An example of a default entry would be "Open" for folders, or generally the bold context menu entry shown at the top. That's the entry Windows will default to when you open the file with a certain extension.
|
||||
|
||||
Media.png
|
||||
|
||||
There can be no default entry at all, and that's perfectly fine. If you don't want your entry to show up as a default one, add an empty REG_SZ value called NeverDefault into the respective shell entry.
|
||||
|
||||
Pass through to Context Menu handlers
|
||||
If you know what context handler you want to invoke on shell entry click, you can specify a so-called Verb Handler.
|
||||
|
||||
Create a new REG_SZ value VerbHandler and specify the context menu handler's UUID as data.
|
||||
|
||||
Context Menu Handler 1.png
|
||||
Context Menu Handler 2.png
|
||||
|
||||
Other options
|
||||
I am not ashamed to admit that I have absolutely zero idea what some of the options do, even though they might sound obvious.
|
||||
|
||||
If you do have the knowledge, please contact me on Twitter: @endermanch or send a mail my way: blogs@enderman.ch
|
||||
|
||||
For example:
|
||||
NoWorkingDirectory - could be hiding the working directory from the app it's starting?
|
||||
CanonicalName
|
||||
CommandStateHandler
|
||||
CommandStateSync
|
||||
Description
|
||||
VerbName
|
||||
|
||||
Deleting entries
|
||||
Pretty much the opposite of adding one.
|
||||
|
||||
Locate the extension (be it a file, or a shell one) you want to target.
|
||||
Sometimes multiple locations can correspond to the same place in the context menu, which might result in the entry remaining in place, even though you've deleted it.
|
||||
Navigate to the shell key.
|
||||
Find the shell entry you want to delete. You can find out the application behind it by looking into the default value of the command key.
|
||||
Delete the key.
|
||||
Sorting entries
|
||||
Now that's the funniest one. I even memed about it on Twitter.
|
||||
The only way to sort the context menu is via the alphabetical order of its respective shell entries in the registry. Yes. I am not kidding. Let the screenshots speak for themselves.
|
||||
|
||||
Alphabetical Sort 1.png
|
||||
Alphabetical Sort 2.png
|
||||
|
||||
Yeah... I'd rather not comment that.
|
||||
|
||||
Position override
|
||||
Another silly and janky feature that has a limited amount of use cases. You can manually override the position of your context menu item to be... either at the very top, or the very bottom. Not much better than alphabetical sorting if you ask me...
|
||||
|
||||
In your entry under shell, create a REG_SZ value called Position.
|
||||
If you want to force the context menu handler to the top, put Top as data, otherwise put Bottom.
|
||||
|
||||
Sometimes this may be necessary, as, for example, Microsoft dumps their regular alphabetical sorting strategy at the bottom of the context menu, where display and personalization settings reside. They simply specify the Bottom position for each of the entries. If I recall correctly, they're still sorted alphabetically between themselves, as position override takes precedence.
|
||||
|
||||
Position Override.png
|
||||
|
||||
Bottom line
|
||||
Some of the default shell entries like Powershell have specific keys (for the sake of that example, ProgrammaticAccessOnly) to control their appearance context menu.
|
||||
|
||||
As a rule of thumb, every time you're dealing with existing shell entries, I REALLY suggest you to search for a way to modify them online before resorting to any of the general cases I've described above. If you can't find anything even on the Internet, well, anything works.
|
||||
|
||||
Uneditable entries
|
||||
As for the Context Menu handlers, there's actually not much you can do. They're located under the shellex\ContextMenuHandlers subkey.
|
||||
|
||||
Context Menu Handler 3.png
|
||||
|
||||
Most of the times, the only data they contain is a lonely UUID. That doesn't help much.
|
||||
|
||||
Context Menu Handler 4.png
|
||||
|
||||
So, to remove the Context Menu handler, you should just delete the subkey under ContextMenuHandlers. I strongly suggest you save the UUID it contained so that you could roll back at any time later.
|
||||
|
||||
Context Menu Handler 5.png
|
||||
|
||||
Adding your own... Well, that's complicated. You actually have to write your own handler in a language supporting Windows API or any sort of an abstraction of it. For example, C# is a decent choice. I don't suggest you waste time on this, unless you're making full-on software that you want to make more accessible to Windows users.
|
||||
|
||||
Dealing with locked keys
|
||||
If the registry key is locked and you don't have write permissions for it, it's owned by TrustedInstaller and you have to manually reclaim ownership of the key and allow yourself to make edits to it.
|
||||
|
||||
Optimal way: Using Process Hacker with the TrustedInstaller plugin or Winaero Tweaker, run regedit as Trusted Installer and perform necessary tweaks.
|
||||
|
||||
General solution:
|
||||
|
||||
Navigate to the key you want to take ownership of
|
||||
Open the Properties window
|
||||
Head over to the Advanced tab
|
||||
At the top of the Advanced Security Settings it states Owner: TrustedInstaller. Click Change
|
||||
Input your username into the Enter the object name to select (examples) text area.
|
||||
Click OK
|
||||
Check the Replace owner on subcontainers and objects checkbox
|
||||
Check the Replace all child object permission entries checkbox
|
||||
Click OK
|
||||
Select Administrators (PC_NAME\Administrators) in the group selector and then check Full Control box in the Permissions window down below
|
||||
Click OK
|
||||
After performing above steps, you must have gotten full and almost complete (some registry values and keys are still protected after the general procedure) access to the desired key.
|
||||
|
||||
Registry paths involved
|
||||
Bottom line of desktop:
|
||||
Computer\HKEY_CLASSES_ROOT\DesktopBackground
|
||||
Desktop Background.png
|
||||
|
||||
Folder Background
|
||||
Computer\HKEY_CLASSES_ROOT\Directory\Background\shell
|
||||
Directory Background.png
|
||||
|
||||
Folder
|
||||
Computer\HKEY_CLASSES_ROOT\Directory\shell
|
||||
Folder.png
|
||||
|
||||
Folder Item (extends Folder)
|
||||
Computer\HKEY_CLASSES_ROOT\Folder
|
||||
File Item.png
|
||||
|
||||
Drive
|
||||
Computer\HKEY_CLASSES_ROOT\Drive
|
||||
Drive.png
|
||||
|
||||
Library folder
|
||||
Computer\HKEY_CLASSES_ROOT\LibraryFolder
|
||||
Library Item.png
|
||||
|
||||
Library background inherits from Folder Background, but can't parse the %V macro, which results in the following error. This is Microsoft's fault. To my knowledge, there is no way to fix that.
|
||||
Library Background.png
|
||||
Explorer Error.png
|
||||
|
||||
All Files
|
||||
Computer\HKEY_CLASSES_ROOT\*
|
||||
All Files.png
|
||||
|
||||
All Filesystem Objects (yep, that's different from All Files - it's a generalization that includes drives and symbols)
|
||||
Computer\HKEY_CLASSES_ROOT\AllFilesystemObjects
|
||||
All Filesystem Objects.png
|
||||
|
||||
Text Files
|
||||
Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\text
|
||||
|
||||
Image Files
|
||||
Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\image
|
||||
|
||||
Document Files
|
||||
Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\document
|
||||
|
||||
Audio Files
|
||||
Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\audio
|
||||
|
||||
Video Files
|
||||
Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\video
|
||||
|
||||
Cleaning up the context menu
|
||||
Most obnoxious default entries can be cleaned up using Winaero Tweaker. I suggest you use it to make the process way more bearable for yourself.
|
||||
|
||||
Winaero 1.png
|
||||
Winaero 2.png
|
||||
|
||||
Areas Winaero Tweaker does not reach
|
||||
Remove "Open PowerShell window here"
|
||||
Navigate to Computer\HKEY_CLASSES_ROOT\Directory\shell\Powershell
|
||||
Unlock key (The Windows Context Menu > Dealing with locked keys)
|
||||
Create a new REG_SZ value ProgrammaticAccessOnly without any data
|
||||
Navigate to Computer\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell
|
||||
Unlock key
|
||||
Create a new REG_SZ value ProgrammaticAccessOnly without any data
|
||||
Remove "Open Linux shell here"
|
||||
Navigate to Computer\HKEY_CLASSES_ROOT\Directory\shell\WSL
|
||||
Unlock key
|
||||
Delete WSL key altogether
|
||||
Navigate to Computer\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell
|
||||
Unlock key
|
||||
Create a new REG_SZ value ProgrammaticAccessOnly without any data
|
||||
Edit the "Edit" text context menu command
|
||||
Navigate to Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\text\shell\edit
|
||||
Make sure you've read the article thoroughly, then do anything you want with it.
|
||||
Bottom line
|
||||
No matter how terrible the context menu API might be for Windows, there's still a way to customize it. Hope you found the tricks useful!
|
||||
|
||||
Contact
|
||||
If you've spotted a mistake (be it orthographical, factual or any other) in this article or want to add
|
||||
And third-party software developers adding oil into the dumpster fire aren't helping with it. It's not their fault, though. There's no common standard and almost no documentation for adding entries into the context menu.
|
|
@ -49,8 +49,6 @@ const aliases = <IconAliases>{
|
|||
|
||||
const iconify = <IconSet>{
|
||||
component: (props: any) => {
|
||||
console.log(props)
|
||||
|
||||
const { icon, tag, ...rest } = props
|
||||
const strIcon = icon as string
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ export default defineNuxtConfig({
|
|||
app: {
|
||||
head: {
|
||||
htmlAttrs: {
|
||||
lang: config.locale,
|
||||
lang: config.locale || 'en',
|
||||
},
|
||||
link: [
|
||||
{
|
||||
|
@ -49,7 +49,6 @@ export default defineNuxtConfig({
|
|||
['@nuxtjs/eslint-module', { failOnError: false, lintOnStart: false }],
|
||||
['@nuxtjs/stylelint-module', { failOnError: true, lintOnStart: false }],
|
||||
'@pinia/nuxt',
|
||||
'@nuxtjs/strapi',
|
||||
'@nuxt/content',
|
||||
'vuetify-nuxt-module',
|
||||
'@nuxtjs/google-fonts',
|
||||
|
@ -59,6 +58,11 @@ export default defineNuxtConfig({
|
|||
'nuxt-og-image',
|
||||
'nuxt-link-checker',
|
||||
],
|
||||
content: {
|
||||
markdown: {
|
||||
remarkPlugins: ['remark-reading-time'],
|
||||
},
|
||||
},
|
||||
nitro: {
|
||||
prerender: {
|
||||
crawlLinks: true,
|
||||
|
@ -107,11 +111,8 @@ export default defineNuxtConfig({
|
|||
defaultSet: 'custom',
|
||||
},
|
||||
defaults: {
|
||||
VApp: {
|
||||
class: 'ender-app',
|
||||
},
|
||||
VBtn: {
|
||||
class: 'text-none',
|
||||
style: 'text-transform: none; letter-spacing: normal;',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
File diff suppressed because it is too large
Load Diff
12
package.json
12
package.json
|
@ -21,8 +21,7 @@
|
|||
"@nuxtjs/eslint-config-typescript": "^12.1.0",
|
||||
"@nuxtjs/eslint-module": "^4.1.0",
|
||||
"@nuxtjs/google-fonts": "^3.1.3",
|
||||
"@nuxtjs/strapi": "^1.11.0",
|
||||
"@nuxtjs/stylelint-module": "^5.1.0",
|
||||
"@nuxtjs/stylelint-module": "^5.2.0",
|
||||
"@pinia/nuxt": "^0.5.1",
|
||||
"@typescript-eslint/parser": "^6.19.1",
|
||||
"animate.css": "latest",
|
||||
|
@ -33,7 +32,7 @@
|
|||
"eslint-plugin-nuxt": "^4.0.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"iconify-icon": "^1.0.8",
|
||||
"nuxt": "^3.9.3",
|
||||
"nuxt": "^3.10.2",
|
||||
"nuxt-link-checker": "^3.0.0-rc.6",
|
||||
"nuxt-og-image": "^3.0.0-rc.29",
|
||||
"nuxt-simple-robots": "^4.0.0-rc.13",
|
||||
|
@ -42,9 +41,9 @@
|
|||
"pinia": "^2.1.7",
|
||||
"prettier": "^3.2.4",
|
||||
"sass": "^1.70.0",
|
||||
"stylelint": "^15.11.0",
|
||||
"stylelint": "^16.2.1",
|
||||
"stylelint-config-recommended-vue": "^1.5.0",
|
||||
"stylelint-config-standard-scss": "^11.0.0",
|
||||
"stylelint-config-standard-scss": "^13.0.0",
|
||||
"typescript": "^5.3.3",
|
||||
"vue": "^3.4.15",
|
||||
"vue-router": "^4.2.5",
|
||||
|
@ -54,6 +53,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@date-io/date-fns": "^3.0.0",
|
||||
"date-fns": "^3.3.1"
|
||||
"date-fns": "^3.3.1",
|
||||
"remark-reading-time": "^2.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
<script setup lang="ts">
|
||||
const config = useAppConfig()
|
||||
const meta = {
|
||||
title: 'Page not found',
|
||||
description:
|
||||
'The page you are looking for does not exist. It might have been removed, had its name changed, or is temporarily unavailable.',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: config.url,
|
||||
}
|
||||
|
||||
useSeoMeta({
|
||||
title: meta.title,
|
||||
description: meta.description,
|
||||
ogTitle: meta.title,
|
||||
ogDescription: meta.description,
|
||||
ogImage: meta.image,
|
||||
ogUrl: meta.url,
|
||||
ogType: 'website',
|
||||
twitterTitle: meta.title,
|
||||
twitterDescription: meta.description,
|
||||
twitterImage: meta.image,
|
||||
twitterCard: 'summary',
|
||||
})
|
||||
|
||||
useHead({
|
||||
title: 'Page not found',
|
||||
htmlAttrs: {
|
||||
lang: config.locale || 'en',
|
||||
},
|
||||
link: [
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/x-icon',
|
||||
href: '/favicon.ico',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const error = {
|
||||
http_code: 200,
|
||||
title: config.title,
|
||||
url: config.url,
|
||||
locale: config.locale || 'en',
|
||||
data: {
|
||||
path: '/error.vue',
|
||||
content: {
|
||||
title: 'Page not found!',
|
||||
description:
|
||||
"The page you are looking for does not exist. However, no 404 error has occurred, as you're browsing through a web application loaded into memory and HTTP error codes make no sense here.",
|
||||
},
|
||||
},
|
||||
slug: useRoute().params.slug,
|
||||
build: config.build,
|
||||
}
|
||||
const errorString = JSON.stringify(error, null, 2)
|
||||
const buffer = ref('')
|
||||
|
||||
const startTime = Date.now()
|
||||
const dt = 20
|
||||
|
||||
let i = 0
|
||||
|
||||
function type() {
|
||||
if (Date.now() > startTime + dt * i) buffer.value += errorString[i++]
|
||||
if (i < errorString.length) requestAnimationFrame(type)
|
||||
}
|
||||
|
||||
onMounted(() => setTimeout(type, 1500))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3 class="alex">Page not found!</h3>
|
||||
<pre class="pre-wrap">{{ buffer }}<span class="blinker">▊</span></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.blinker {
|
||||
animation: blinker 1s steps(2, start) infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
from {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
to {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,4 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
const config = useAppConfig()
|
||||
const age =
|
||||
new Date(Number(new Date()) - Number(new Date('2003-09-18'))).getFullYear() -
|
||||
1970
|
||||
|
@ -6,8 +7,8 @@ const age =
|
|||
const meta = {
|
||||
title: 'About Me',
|
||||
description: `Nice to meet you!~ I'm Andrew, a ${age}-year-old developer from Kaluga, Russia.`,
|
||||
image: 'https://enderman.ch/images/logo.png',
|
||||
url: 'https://enderman.ch/about',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: `${config.url}/about`,
|
||||
}
|
||||
|
||||
useSeoMeta({
|
||||
|
@ -27,7 +28,7 @@ useSeoMeta({
|
|||
useHead({
|
||||
title: 'About',
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
lang: config.locale || 'en',
|
||||
},
|
||||
link: [
|
||||
{
|
||||
|
@ -82,6 +83,16 @@ useHead({
|
|||
thinking for quite some time, and then concluded that adding «ch» at the
|
||||
end sounded pretty good. I was 14 at the time of making that decision.
|
||||
</li>
|
||||
<li>
|
||||
<strong
|
||||
>When will you make a new video? What happened to your
|
||||
schedule?</strong
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
One day. Haven't been particularly motivated lately, but life's busy. 🙁
|
||||
I'm alive and well, though.
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<script setup lang="ts">
|
||||
const { slug } = useRoute().params
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<article
|
||||
class="scrollbar fade-mask-sm d-flex flex-column gap-3 flex-grow-1 overflow-x-hidden overflow-y-auto py-sm-4 pe-sm-3"
|
||||
>
|
||||
<ContentDoc :path="`/blog/${slug}`">
|
||||
<template #default="{ doc }">
|
||||
<div class="post-preamble">
|
||||
<h3 class="alex">{{ doc.title }}</h3>
|
||||
<img
|
||||
draggable="false"
|
||||
:src="doc.thumbnail"
|
||||
:alt="doc.title"
|
||||
class="post-preamble-thumb rounded-4"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="post-content">
|
||||
<ContentRenderer :value="doc" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #not-found>
|
||||
<div
|
||||
class="d-flex flex-column justify-content-center align-items-center gap-3"
|
||||
>
|
||||
<span>Blog post not found 🙁</span>
|
||||
<NuxtLink to="/blog" class="link-hi">Back to blog</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
</ContentDoc>
|
||||
</article>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,80 @@
|
|||
<script setup lang="ts">
|
||||
import { formatDate } from 'date-fns'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section>
|
||||
<h3 class="alex">Recent posts</h3>
|
||||
<ContentList
|
||||
:query="{
|
||||
path: '/blog',
|
||||
where: [{ draft: false }],
|
||||
limit: 3,
|
||||
sort: [{ created: -1 }],
|
||||
}"
|
||||
>
|
||||
<template #default="{ list }">
|
||||
<div class="d-grid gap-3">
|
||||
<NuxtLink
|
||||
v-for="post in list"
|
||||
:key="post._path"
|
||||
:to="post._path"
|
||||
class="no-decoration"
|
||||
>
|
||||
<article
|
||||
class="post-box d-flex flex-column flex-md-row gap-3 rounded-4 h-100"
|
||||
>
|
||||
<div class="post-thumb">
|
||||
<img
|
||||
draggable="false"
|
||||
:src="post.thumbnail"
|
||||
:alt="post.title"
|
||||
class="rounded-4"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="post-title alex">{{ post.title }}</h3>
|
||||
<p class="post-description">{{ post.description }}</p>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6 d-flex flex-row gap-1 align-items-center">
|
||||
<iconify-icon icon="mdi:calendar" />
|
||||
<small class="nobr">
|
||||
{{ formatDate(post.created, 'LLLL do, y – HH:mm') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="post.updated" class="row">
|
||||
<div class="col-12 d-flex flex-row gap-1 align-items-center">
|
||||
<iconify-icon icon="mdi:pencil" />
|
||||
<small class="nobr">
|
||||
{{ formatDate(post.updated, 'LLLL do, y – HH:mm') }}
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="post-pocket d-flex flex-row gap-1 align-items-center">
|
||||
<iconify-icon icon="mdi:clock-outline" />
|
||||
<small class="nobr font-monospace font-small">
|
||||
{{ post.readingTime.text }}
|
||||
</small>
|
||||
</div>
|
||||
</article>
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #not-found>
|
||||
<div
|
||||
class="d-flex flex-column justify-content-center align-items-center gap-3"
|
||||
>
|
||||
<span>No posts found 🙁</span>
|
||||
<NuxtLink to="/" class="link-hi">Back to index</NuxtLink>
|
||||
</div>
|
||||
</template>
|
||||
</ContentList>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -3,7 +3,7 @@ const config = useAppConfig()
|
|||
const meta = {
|
||||
title: 'Enderman',
|
||||
description: config.description,
|
||||
image: 'https://enderman.ch/images/logo.png',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: config.url,
|
||||
}
|
||||
|
||||
|
@ -43,9 +43,9 @@ useHead({
|
|||
I'm <strong>Enderman</strong> – a software engineer, a malware
|
||||
enthusiast and most importantly, a weird tall creature. I have over 300K
|
||||
subscribers on
|
||||
<a class="link-hi" href="https://go.enderman.ch/youtube">YouTube</a> and
|
||||
<a class="link-hi" :href="`${config.shortener}/youtube`">YouTube</a> and
|
||||
over 20K followers on
|
||||
<a class="link-hi" href="https://go.enderman.ch/twitter">Twitter</a>.
|
||||
<a class="link-hi" :href="`${config.shortener}/twitter`">Twitter</a>.
|
||||
Sometimes I wish there were 48 hours in a day.
|
||||
</p>
|
||||
<div class="row g-3">
|
||||
|
@ -57,7 +57,7 @@ useHead({
|
|||
<strong>C/C++</strong> for desktop applications and any common
|
||||
backend stack with framework-loaded TypeScript for web. You can take
|
||||
a look at my code on
|
||||
<a class="link-hi" href="https://go.enderman.ch/github">GitHub</a>.
|
||||
<a class="link-hi" :href="`${config.shortener}/github`">GitHub</a>.
|
||||
</li>
|
||||
<li>
|
||||
I have the most unnecessary, yet fascinating knowledge about
|
||||
|
@ -70,7 +70,7 @@ useHead({
|
|||
<li>
|
||||
I research and analyze modern malware, educate computer users about
|
||||
it and preserve history. The repository can be found
|
||||
<a class="link-hi" href="https://go.enderman.ch/repository">here</a
|
||||
<a class="link-hi" :href="`${config.shortener}/repository`">here</a
|
||||
>.
|
||||
</li>
|
||||
<li>I make videos for you to enjoy!</li>
|
||||
|
@ -86,7 +86,7 @@ useHead({
|
|||
<li>Philosophy</li>
|
||||
<li>Geopolitics</li>
|
||||
<li>
|
||||
<a class="link-hi" href="https://go.enderman.ch/chess">Chess</a>
|
||||
<a class="link-hi" :href="`${config.shortener}/chess`">Chess</a>
|
||||
</li>
|
||||
<li>Solitude</li>
|
||||
</ul>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
// AI-Generated test data.
|
||||
const config = useAppConfig()
|
||||
const projects = [
|
||||
{
|
||||
name: 'MalwareWatch',
|
||||
|
@ -25,7 +25,7 @@ const projects = [
|
|||
{
|
||||
name: 'Windows XP Keygen / UMSKT',
|
||||
description: 'A key generator for Windows XP.',
|
||||
url: 'https://go.enderman.ch/xpkeygen',
|
||||
url: `${config.shortener}/xpkeygen`,
|
||||
},
|
||||
]
|
||||
|
||||
|
@ -33,8 +33,8 @@ const meta = {
|
|||
title: "Enderman's Projects",
|
||||
description:
|
||||
"A display of my largest projects. Some of them aren't published on YouTube.",
|
||||
image: 'https://enderman.ch/images/logo.png',
|
||||
url: 'https://enderman.ch/projects',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: `${config.url}/projects`,
|
||||
}
|
||||
|
||||
useSeoMeta({
|
||||
|
@ -54,7 +54,7 @@ useSeoMeta({
|
|||
useHead({
|
||||
title: 'Projects',
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
lang: config.locale || 'en',
|
||||
},
|
||||
link: [
|
||||
{
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<script setup lang="ts">
|
||||
const config = useAppConfig()
|
||||
const fqdn = config.url.split('//')[1]
|
||||
|
||||
const socials = [
|
||||
{
|
||||
name: 'YouTube',
|
||||
url: 'https://go.enderman.ch/youtube',
|
||||
url: `${config.shortener}/youtube`,
|
||||
icon: {
|
||||
name: 'logos:youtube-icon',
|
||||
color: 'white',
|
||||
|
@ -10,7 +13,7 @@ const socials = [
|
|||
},
|
||||
{
|
||||
name: 'Andrew',
|
||||
url: 'https://go.enderman.ch/andrew',
|
||||
url: `${config.shortener}/andrew`,
|
||||
icon: {
|
||||
name: 'logos:youtube-icon',
|
||||
color: 'white',
|
||||
|
@ -18,7 +21,7 @@ const socials = [
|
|||
},
|
||||
{
|
||||
name: 'Twitch',
|
||||
url: 'https://go.enderman.ch/twitch',
|
||||
url: `${config.shortener}/twitch`,
|
||||
icon: {
|
||||
name: 'fa:twitch',
|
||||
color: '#B68CFF',
|
||||
|
@ -26,7 +29,7 @@ const socials = [
|
|||
},
|
||||
{
|
||||
name: 'TikTok',
|
||||
url: 'https://go.enderman.ch/tiktok',
|
||||
url: `${config.shortener}/tiktok`,
|
||||
icon: {
|
||||
name: 'logos:tiktok-icon',
|
||||
color: 'white',
|
||||
|
@ -34,7 +37,7 @@ const socials = [
|
|||
},
|
||||
{
|
||||
name: 'Twitter',
|
||||
url: 'https://go.enderman.ch/twitter',
|
||||
url: `${config.shortener}/twitter`,
|
||||
icon: {
|
||||
name: 'logos:twitter',
|
||||
color: 'white',
|
||||
|
@ -42,7 +45,7 @@ const socials = [
|
|||
},
|
||||
{
|
||||
name: 'Telegram',
|
||||
url: 'https://go.enderman.ch/telegram',
|
||||
url: `${config.shortener}/telegram`,
|
||||
icon: {
|
||||
name: 'logos:telegram',
|
||||
color: 'white',
|
||||
|
@ -50,7 +53,7 @@ const socials = [
|
|||
},
|
||||
{
|
||||
name: 'Discord',
|
||||
url: 'https://go.enderman.ch/discord',
|
||||
url: `${config.shortener}/discord`,
|
||||
icon: {
|
||||
name: 'logos:discord-icon',
|
||||
color: 'white',
|
||||
|
@ -58,7 +61,7 @@ const socials = [
|
|||
},
|
||||
{
|
||||
name: 'GitHub',
|
||||
url: 'https://go.enderman.ch/github',
|
||||
url: `${config.shortener}/github`,
|
||||
icon: {
|
||||
name: 'fa:github',
|
||||
color: 'white',
|
||||
|
@ -66,7 +69,7 @@ const socials = [
|
|||
},
|
||||
{
|
||||
name: 'Steam',
|
||||
url: 'https://go.enderman.ch/steam',
|
||||
url: `${config.shortener}/steam`,
|
||||
icon: {
|
||||
name: 'fa:steam',
|
||||
color: 'white',
|
||||
|
@ -78,8 +81,8 @@ const meta = {
|
|||
title: 'Enderman Online',
|
||||
description:
|
||||
'Discover me on your favorite social media and find ways to contact me here.',
|
||||
image: 'https://enderman.ch/images/logo.png',
|
||||
url: 'https://enderman.ch/projects',
|
||||
image: `${config.url}/images/logo.png`,
|
||||
url: `${config.url}/projects`,
|
||||
}
|
||||
|
||||
useSeoMeta({
|
||||
|
@ -99,7 +102,7 @@ useSeoMeta({
|
|||
useHead({
|
||||
title: 'Socials',
|
||||
htmlAttrs: {
|
||||
lang: 'en',
|
||||
lang: config.locale || 'en',
|
||||
},
|
||||
link: [
|
||||
{
|
||||
|
@ -140,11 +143,11 @@ useHead({
|
|||
Personal:
|
||||
<EMail
|
||||
class="link-hi"
|
||||
address="contact@enderman.ch"
|
||||
:address="`contact@${fqdn}`"
|
||||
subject="Hey Enderman!"
|
||||
/><br />
|
||||
Manager: <EMail class="link-hi" address="manager@enderman.ch" /><br />
|
||||
Abuse: <EMail class="link-hi" address="abuse@enderman.ch" />
|
||||
Manager: <EMail class="link-hi" :address="`manager@${fqdn}`" /><br />
|
||||
Abuse: <EMail class="link-hi" :address="`abuse@${fqdn}`" />
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
Loading…
Reference in New Issue