Blog slowly looming over
This commit is contained in:
parent
b58407d358
commit
44c60f7069
17
app.vue
17
app.vue
|
@ -3,12 +3,23 @@ const config = useAppConfig()
|
||||||
const theme = useVThemeSSR()
|
const theme = useVThemeSSR()
|
||||||
const vdisp = ref(useVDisplay())
|
const vdisp = ref(useVDisplay())
|
||||||
const { reader } = storeToRefs(usePageStore())
|
const { reader } = storeToRefs(usePageStore())
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
useHead({
|
useHead({
|
||||||
titleTemplate: (chunk?) => {
|
titleTemplate: (chunk?) => {
|
||||||
return chunk === config.title.short
|
if (route.fullPath === '/') return config.title.full
|
||||||
? config.title.full
|
|
||||||
: `${chunk} – ${config.title.short}`
|
console.log(route.fullPath.split('/'))
|
||||||
|
|
||||||
|
if (route.fullPath.split('/').length === 2)
|
||||||
|
switch (route.fullPath.split('/')[1]) {
|
||||||
|
case 'blog':
|
||||||
|
return 'The Enderchest'
|
||||||
|
default:
|
||||||
|
return `${chunk} – ${config.title.short}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return chunk!
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -244,6 +244,10 @@ body {
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background-color: rgb(255 255 255 / 15%);
|
background-color: rgb(255 255 255 / 15%);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-button {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
|
@ -307,6 +311,9 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
.post {
|
.post {
|
||||||
|
scroll-snap-type: both mandatory;
|
||||||
|
scroll-snap-stop: normal;
|
||||||
|
|
||||||
&-box {
|
&-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
@ -363,16 +370,52 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-details {
|
|
||||||
}
|
|
||||||
|
|
||||||
&-preamble {
|
&-preamble {
|
||||||
&-thumb {
|
display: flex;
|
||||||
max-width: 800px;
|
flex-direction: column;
|
||||||
|
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
min-height: 400px;
|
||||||
|
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
background-size: cover;
|
||||||
|
background-attachment: fixed;
|
||||||
|
|
||||||
|
border-radius: 1em;
|
||||||
|
|
||||||
|
scroll-snap-align: end;
|
||||||
|
|
||||||
|
text-shadow: black 1px 1px 7px;
|
||||||
|
|
||||||
|
iconify-icon {
|
||||||
|
filter: drop-shadow(1px 2px 3px rgb(0 0 0 / 100%));
|
||||||
|
}
|
||||||
|
|
||||||
|
&-share {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-control {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&-content {
|
&-content {
|
||||||
|
scroll-snap-align: start;
|
||||||
|
|
||||||
:is(p, li) {
|
:is(p, li) {
|
||||||
a {
|
a {
|
||||||
@extend %link;
|
@extend %link;
|
||||||
|
@ -383,6 +426,21 @@ body {
|
||||||
color: royalblue;
|
color: royalblue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0.25em;
|
||||||
|
|
||||||
|
border-radius: 0.5em;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgb(83 35 162 / 10%);
|
||||||
|
|
||||||
|
transition: 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgb(138 71 245 / 20%);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:is(h1, h2, h3, h4, h5, h6) {
|
:is(h1, h2, h3, h4, h5, h6) {
|
||||||
|
@ -399,8 +457,90 @@ body {
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img {
|
||||||
|
max-width: 100%;
|
||||||
max-height: 400px;
|
max-height: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
padding: 0.75em 1em;
|
||||||
|
|
||||||
|
border-left: 0.25em solid rgb(153 153 255 / 60%);
|
||||||
|
border-top-right-radius: 0.5em;
|
||||||
|
border-bottom-right-radius: 0.5em;
|
||||||
|
|
||||||
|
background-color: rgb(153 153 255 / 10%);
|
||||||
|
|
||||||
|
transition: 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border-left-color: rgb(153 153 255 / 100%);
|
||||||
|
background-color: rgb(153 153 255 / 15%);
|
||||||
|
}
|
||||||
|
|
||||||
|
:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 1em;
|
||||||
|
|
||||||
|
border-radius: 0.5em;
|
||||||
|
background-color: rgb(83 35 162 / 5%);
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
overflow-x: auto;
|
||||||
|
|
||||||
|
code {
|
||||||
|
counter-reset: step;
|
||||||
|
counter-increment: step 0;
|
||||||
|
|
||||||
|
scroll-padding: 0.5em;
|
||||||
|
|
||||||
|
.line::before {
|
||||||
|
content: counter(step);
|
||||||
|
counter-increment: step;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
color: rgb(238 246 250 / 40%);
|
||||||
|
|
||||||
|
width: 1rem;
|
||||||
|
margin-right: 1.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
margin: 0.5em;
|
||||||
|
padding: 0.5em;
|
||||||
|
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
background-color: rgb(153 153 255 / 10%);
|
||||||
|
|
||||||
|
border: 1px solid rgb(153 153 255 / 60%);
|
||||||
|
border-radius: 0.5em;
|
||||||
|
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
transition: 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.button {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,7 +610,7 @@ body {
|
||||||
&-description {
|
&-description {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
|
|
||||||
-webkit-line-clamp: 2;
|
-webkit-line-clamp: 4;
|
||||||
-webkit-box-orient: vertical;
|
-webkit-box-orient: vertical;
|
||||||
|
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
---
|
---
|
||||||
title: The Windows Context Menu – Is It a Lost Cause?
|
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.
|
description: A deep dive into the Windows context menu API and customization options it offers. Shell components for beginners and context menu cleanup.
|
||||||
|
authors: ['Enderman']
|
||||||
created: 2023-08-13T15:51:19Z
|
created: 2023-08-13T15:51:19Z
|
||||||
updated: 2024-02-18T21:32:12Z
|
updated: 2024-02-18T21:32:12Z
|
||||||
draft: false
|
draft: false
|
||||||
tags: ['windows', 'context-menu', 'customization', 'registry', 'shell', 'shell-extensions', 'context-menu-handlers']
|
tags: ['windows', 'context menu', 'customization', 'registry', 'windows tweaks', 'shell', 'shell extensions', 'shell components', 'context menu handlers', 'windows 10', 'windows 11']
|
||||||
thumbnail: '/images/blog/thumbnails/the-windows-context-menu.png'
|
thumbnail: /images/blog/thumbnails/the-windows-context-menu.png
|
||||||
|
ogImage:
|
||||||
|
component: BlogImage
|
||||||
|
props:
|
||||||
|
image: /images/blog/thumbnails/the-windows-context-menu.png
|
||||||
---
|
---
|
||||||
|
|
||||||
# The Windows context menu is... poorly made.
|
# The Windows context menu is... poorly made.
|
||||||
|
@ -63,7 +68,7 @@ upon execution.
|
||||||
There are two main components the context menu is composed of:
|
There are two main components the context menu is composed of:
|
||||||
|
|
||||||
* **Shell entries** — provided locally and managed by the registry;
|
* **Shell entries** — provided locally and managed by the registry;
|
||||||
* **Context menu handlers** — provided and managed by external software;
|
* **Context menu handlers** — provided and managed by external software.
|
||||||
|
|
||||||
Neither of them is documented well, but
|
Neither of them is documented well, but
|
||||||
there is some developer documentation for handlers available [on Microsoft's website](https://go.enderman.ch/Fm4t3).
|
there is some developer documentation for handlers available [on Microsoft's website](https://go.enderman.ch/Fm4t3).
|
||||||
|
@ -97,7 +102,7 @@ In conclusion, there are three types of applications in terms of friendliness to
|
||||||
|
|
||||||
* **Apps using context menu handlers with customizable settings** — the friendliest;
|
* **Apps using context menu handlers with customizable settings** — the friendliest;
|
||||||
* **Apps using shell entries** — customizable through registry hacks;
|
* **Apps using shell entries** — customizable through registry hacks;
|
||||||
* **Apps using context menu handlers without settings** — the entries are uneditable;
|
* **Apps using context menu handlers without settings** — the entries are uneditable.
|
||||||
|
|
||||||
The first case is the simplest and isn't worth talking about —
|
The first case is the simplest and isn't worth talking about —
|
||||||
consult the documentation for the software in question to find all the necessary tweaks.
|
consult the documentation for the software in question to find all the necessary tweaks.
|
||||||
|
@ -154,7 +159,7 @@ for better understanding:
|
||||||
* **Shell extension** is a general term for a plugin for the Windows Explorer — the **context menu handler** is a
|
* **Shell extension** is a general term for a plugin for the Windows Explorer — the **context menu handler** is a
|
||||||
particular case of a shell extension;
|
particular case of a shell extension;
|
||||||
* **Shell component** is a broad term for a component that helps the shell interact with the system — that includes
|
* **Shell component** is a broad term for a component that helps the shell interact with the system — that includes
|
||||||
shell extensions, file extensions, protocols, etc.;
|
shell extensions, file extensions, protocols, etc.
|
||||||
|
|
||||||
If this doesn't show how big of a predicament Microsoft is in with the context menu, I don't know what does.
|
If this doesn't show how big of a predicament Microsoft is in with the context menu, I don't know what does.
|
||||||
I tried my best to keep the technical language as clear as possible. 😒
|
I tried my best to keep the technical language as clear as possible. 😒
|
||||||
|
@ -167,7 +172,7 @@ Entries provided by software — the context menu handlers — are stored under
|
||||||

|

|
||||||
|
|
||||||
Since registry key names are case-insensitive by design in Windows, capitalization plays no role.
|
Since registry key names are case-insensitive by design in Windows, capitalization plays no role.
|
||||||
**The value names, however, are** — keep that in mind, as in some cases it might bite you.
|
**The value names, however, are** — keep that in mind, as in some cases it might bite.
|
||||||
The context menu doesn't seem to care.
|
The context menu doesn't seem to care.
|
||||||
|
|
||||||
_Truly an amazing specification!_
|
_Truly an amazing specification!_
|
||||||
|
@ -189,9 +194,9 @@ No matter how hard I tried, I could not find the exact specification of the inco
|
||||||
My suggestion is to treat the default `command` value as **raw command line** (e.g. Run box),
|
My suggestion is to treat the default `command` value as **raw command line** (e.g. Run box),
|
||||||
with an exception to the following two macros:
|
with an exception to the following two macros:
|
||||||
* `%1` — the absolute file path without quotes
|
* `%1` — the absolute file path without quotes
|
||||||
If you right-click `C:\Users\bleh\p.txt`, execute the shell entry,
|
When the shell entry is executed after a right-click on `C:\Users\bleh\p.txt`,
|
||||||
the `%1` in the context of the command line will be equivalent to `C:\Users\bleh\p.txt`;
|
the `%1` in the context of the command line is equivalent to `C:\Users\bleh\p.txt`;
|
||||||
* `%V` — the absolute directory path without quotes;
|
* `%V` — the absolute directory path without quotes.
|
||||||
|
|
||||||
::card
|
::card
|
||||||
---
|
---
|
||||||
|
@ -210,7 +215,7 @@ The directory macro `%V` also sometimes falls flat depending on the shell entry
|
||||||
|
|
||||||
On rare occasions, the default value is left unset, with `DelegateExecute` alongside it containing a GUID.
|
On rare occasions, the default value is left unset, with `DelegateExecute` alongside it containing a GUID.
|
||||||
When that's the case, the command handler is implemented through the `IExecuteCommand` COM interface,
|
When that's the case, the command handler is implemented through the `IExecuteCommand` COM interface,
|
||||||
and you cannot customize it.
|
and hence cannot be customized by normal means.
|
||||||
|
|
||||||
**Why is that a thing?** Ask me a simpler question. It could be a compatibility layer for C, but your guess is as good as mine.
|
**Why is that a thing?** Ask me a simpler question. It could be a compatibility layer for C, but your guess is as good as mine.
|
||||||
|
|
||||||
|
@ -281,17 +286,20 @@ you should use the `REG_SZ` value called `Icon` inside the respective `shell` ke
|
||||||
|
|
||||||
It uses that unmistakably distinct and clumsy **WinAPI resource syntax** to specify a direct link to the icon within
|
It uses that unmistakably distinct and clumsy **WinAPI resource syntax** to specify a direct link to the icon within
|
||||||
a file containing resources.
|
a file containing resources.
|
||||||
|
|
||||||
```csv
|
```csv
|
||||||
PATH_TO_FILE_WITH_ICON_RESOURCES,INDEX
|
PATH_TO_FILE_WITH_ICON_RESOURCES,INDEX
|
||||||
```
|
```
|
||||||
|
|
||||||
If you want to select the first icon (in that case, the `INDEX` part is 0),
|
If you want to select the first icon (in that case, the `INDEX` part is 0),
|
||||||
the `INDEX` part can be omitted completely:
|
the `INDEX` part can be omitted completely:
|
||||||
|
|
||||||
```csv
|
```csv
|
||||||
PATH_TO_FILE_WITH_ICON_RESOURCES
|
PATH_TO_FILE_WITH_ICON_RESOURCES
|
||||||
```
|
```
|
||||||
|
|
||||||
The following expressions are completely valid and are going to work:
|
The following expressions are completely valid and are going to work:
|
||||||
|
|
||||||
```csv
|
```csv
|
||||||
"C:\Windows\system32\cmd.exe"
|
"C:\Windows\system32\cmd.exe"
|
||||||
C:\Windows\regedit.exe
|
C:\Windows\regedit.exe
|
||||||
|
@ -302,6 +310,7 @@ shell32.dll,3
|
||||||
SHELL32.DLL,10
|
SHELL32.DLL,10
|
||||||
"C:\Windows\system32\SHELL32.DLL",9
|
"C:\Windows\system32\SHELL32.DLL",9
|
||||||
```
|
```
|
||||||
|
|
||||||
You can omit the quotes and absolute paths depending on the `%PATH%` variable that unholy concoction of an API uses.
|
You can omit the quotes and absolute paths depending on the `%PATH%` variable that unholy concoction of an API uses.
|
||||||
**Spoiler:** they don't match against the system `%PATH%`.
|
**Spoiler:** they don't match against the system `%PATH%`.
|
||||||
Only God knows what paths it's able to parse by default.
|
Only God knows what paths it's able to parse by default.
|
||||||
|
@ -381,11 +390,11 @@ Please tag me on [Twitter](https://go.enderman.ch/twitter) or [send a mail](mail
|
||||||
have the knowledge.
|
have the knowledge.
|
||||||
|
|
||||||
**A few examples:**
|
**A few examples:**
|
||||||
* `CanonicalName`
|
* `CanonicalName`;
|
||||||
* `CommandStateHandler`
|
* `CommandStateHandler`;
|
||||||
* `CommandStateSync`
|
* `CommandStateSync`;
|
||||||
* `Description`
|
* `Description`;
|
||||||
* `VerbName`
|
* `VerbName`.
|
||||||
|
|
||||||
#### Deleting entries
|
#### Deleting entries
|
||||||
Even simpler — it's the opposite of adding one.
|
Even simpler — it's the opposite of adding one.
|
||||||
|
@ -400,86 +409,195 @@ Even simpler — it's the opposite of adding one.
|
||||||
which might result in the item remaining in place despite the deletion.
|
which might result in the item remaining in place despite the deletion.
|
||||||
|
|
||||||
#### Sorting entries
|
#### Sorting entries
|
||||||
Now that's the funniest one. I even memed about it on Twitter.
|
That's easily the most hilarious procedure of them all. I even [tweeted](https://go.enderman.ch/3hcO9) about it.
|
||||||
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.
|
The only way to sort the context menu items is...
|
||||||
|
**by managing the alphabetical order of their respective shell entries in the registry**.
|
||||||
|
Seriously, I am not kidding.
|
||||||
|
Let the screenshots speak for themselves.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Yeah... I'd rather not comment on that.
|
_Yeah... I'd rather leave all the obscene comments aside._
|
||||||
|
|
||||||
##### Position override
|
##### Position override
|
||||||
Another silly and janky feature that has a limited number of use cases.
|
Another silly feature with a limited number of use cases
|
||||||
You can manually override the position of your context menu item to be...
|
that's likely been created due to all prior mess Microsoft has created.
|
||||||
|
It's possible to manually override the position of a context menu item to be...
|
||||||
either at the very top, or the very bottom.
|
either at the very top, or the very bottom.
|
||||||
Not much better than alphabetical sorting if you ask me...
|
_Still doesn't top off the alphabetical sorting between the registry peers..._
|
||||||
|
|
||||||
In your entry under shell, create a REG_SZ value called Position.
|
In the `shell` subkey under the respective shell entry, 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.
|
To enforce the context menu item to appear at the very top,
|
||||||
|
insert `Top` into the value data, otherwise insert `Bottom`.
|
||||||
|
|
||||||
Sometimes this may be necessary, as, for example, Microsoft dumps their regular alphabetical sorting strategy at
|
Sometimes directly enforcing the position happens to be necessary.
|
||||||
the bottom of the context menu, where display and personalization settings reside.
|
For instance, Microsoft dumps the usual sorting strategy at the very bottom of the context menu,
|
||||||
They simply specify the Bottom position for each of the entries.
|
where the display and personalization settings reside.
|
||||||
If I recall correctly, they're still sorted alphabetically between themselves, as position override takes precedence.
|
They simply set the `Bottom` position for each of the entries.
|
||||||
|
|
||||||
|
**What happens when there are multiple shell entries enforced to be in a set position?**
|
||||||
|
The alphabetical sorting makes a comeback,
|
||||||
|
it occurs between the shell entries with overridden positions just the same way.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
##### Bottom line
|
### Context menu handlers
|
||||||
Some of the default shell entries like Powershell have specific keys
|
Compared to shell entries, there isn't much to be tweaked via registry in context menu handlers.
|
||||||
(for the sake of that example, `ProgrammaticAccessOnly`) to control their appearance context menu.
|
Their general location is the `ContextMenuHandlers` subkey under the `shellex` key in the respective shell entry.
|
||||||
|
|
||||||
As a rule of thumb, every time you're dealing with existing shell entries,
|
|
||||||
I REALLY suggest that you 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.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Most of the time, the only data they contain is a lonely GUID. That doesn't help much.
|
Most of the time, the only piece of data they contain is a lonely GUID of the respective handler.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
So, to remove the Context Menu handler, you should delete the subkey under ContextMenuHandlers.
|
#### Removing handlers
|
||||||
I strongly suggest you save the GUID it contained so that you could roll back at any time later.
|
|
||||||
|
A context menu handler always directly corresponds to some child under the `ContextMenuHandlers` key.
|
||||||
|
Therefore, all that has to be done to remove a handler is removal of the **respective child key**.
|
||||||
|
|
||||||
|
I strongly suggest writing **the GUID** and **the location** of the removed entry down somewhere,
|
||||||
|
so that there is a way to roll back any time later.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Adding your own...
|
#### Adding handlers
|
||||||
Well, that's complicated.
|
|
||||||
You actually have to write your own handler in a language supporting Windows API or any sort of abstraction of it.
|
Adding a custom handler is complicated.
|
||||||
For example, C# is a decent choice.
|
It's not as trivial as adding a shell entry —
|
||||||
I don't suggest you waste time on this,
|
it requires implementing a handler interface using a language
|
||||||
unless you're making full-on software that you want to make more accessible to Windows users.
|
supporting Windows API or an abstraction layer on top of it.
|
||||||
|
C# would be a decent choice precisely for this task.
|
||||||
|
|
||||||
|
However, it's just a waste of time to implement a handler for a single shell entry,
|
||||||
|
unless there's software to go along with it.
|
||||||
|
The idea behind context menu handlers is to **make native application experience nicer** for Windows users.
|
||||||
|
|
||||||
|
In other words, Microsoft failed to implement an accessible API for the context menu,
|
||||||
|
so they did what they do best — shifted the responsibility to the developers writing third-party software.
|
||||||
|
|
||||||
|
This is how a partial implementation of the `IContextMenu` interface in C++ looks like:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
STDMETHODIMP CShellExtension::InvokeCommand(LPCMINVOKECOMMANDINFO lpcmi) {
|
||||||
|
BOOL fEx = FALSE;
|
||||||
|
BOOL fUnicode = FALSE;
|
||||||
|
|
||||||
|
if(lpcmi->cbSize == sizeof(CMINVOKECOMMANDINFOEX)) {
|
||||||
|
fEx = TRUE;
|
||||||
|
if (lpcmi->fMask & CMIC_MASK_UNICODE) fUnicode = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fUnicode && HIWORD(lpcmi->lpVerb)) {
|
||||||
|
if (StrCmpIA(lpcmi->lpVerb, m_pszVerb)) return E_FAIL;
|
||||||
|
}
|
||||||
|
else if (fUnicode && HIWORD(((CMINVOKECOMMANDINFOEX *) lpcmi)->lpVerbW)) {
|
||||||
|
if (StrCmpIW(((CMINVOKECOMMANDINFOEX *)lpcmi)->lpVerbW, m_pwszVerb)) return E_FAIL;
|
||||||
|
}
|
||||||
|
else if (LOWORD(lpcmi->lpVerb) != IDM_DISPLAY) {
|
||||||
|
return E_FAIL;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
MessageBox(
|
||||||
|
lpcmi->hwnd,
|
||||||
|
"The File Name",
|
||||||
|
"File Name",
|
||||||
|
MB_OK | MB_ICONINFORMATION
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `InvokeCommand()` method is called when a user clicks a context menu item.
|
||||||
|
I've taken that example from [Microsoft](https://go.enderman.ch/TArYX).
|
||||||
|
|
||||||
|
That whole thing isn't too bad — the logic is far more robust and understandable
|
||||||
|
compared to its surface-level registry counterpart, but it certainly requires getting used to WinAPI.
|
||||||
|
|
||||||
|
**By definition, it's a requirement to implement interfaces entirely.**
|
||||||
|
However, since we're dealing with Microsoft,
|
||||||
|
the «interface» in question is actually an _abstract class_ and can be implemented partially.
|
||||||
|
|
||||||
|
The `IContextMenu` interface has three methods:
|
||||||
|
* `GetCommandString()` — retrieves the canonical name of the command (optional);
|
||||||
|
* `InvokeCommand()` — handles the command (required);
|
||||||
|
* `QueryContextMenu()` — adds items to the context menu (required).
|
||||||
|
|
||||||
|
The `IContextMenu` interface is the most common one, but there are others,
|
||||||
|
such as `IContextMenu2` and `IContextMenu3`.
|
||||||
|
Essentially, this is how the context menu handlers work inside.
|
||||||
|
|
||||||
|
While all of this is just a brief overview of the API,
|
||||||
|
you can learn everything about them and more in the [Shell Developer's Guide](https://go.enderman.ch/W50xJ).
|
||||||
|
|
||||||
## Dealing with locked keys
|
## Dealing with locked keys
|
||||||
If the registry key is locked, and you don't have write permissions for it, it's owned by TrustedInstaller,
|
If the registry key is locked, the user you're logged in as is excluded from the ACL.
|
||||||
and you have to manually reclaim ownership of the key and allow yourself to make edits to it.
|
Commonly, when the key is locked, it's owned by `TrustedInstaller`.
|
||||||
|
When that's the case, it's necessary to reclaim ownership of the key to allow making edits to the ACL.
|
||||||
|
There are plenty of ways to do that, I will list two common ones.
|
||||||
|
|
||||||
Optimal way: Using Process Hacker with the TrustedInstaller plugin or Winaero Tweaker,
|
### Using external tools
|
||||||
run regedit as Trusted Installer and perform the necessary tweaks.
|
The idea here is to run `regedit` — the registry editor as the `TrustedInstaller` user directly.
|
||||||
|
You can think of `TrustedInstaller` as the Windows equivalent of `root` in Unix-like systems —
|
||||||
|
the superuser.
|
||||||
|
|
||||||
General solution:
|
While Windows generally has more granular control over permissions,
|
||||||
|
and the idea of having a superuser — **the ultimate authority of the system** — is less prominent and unannounced,
|
||||||
|
`TrustedInstaller` is the closest object to a superuser Windows has.
|
||||||
|
|
||||||
Navigate to the key you want to take ownership of
|
For those wondering, I've already explained [here](https://go.enderman.ch/o2KGt) why
|
||||||
Open the «Properties» window
|
«sudo for Windows» makes no sense.
|
||||||
Head over to the Advanced tab
|
|
||||||
At the top of the Advanced Security Settings it states Owner: TrustedInstaller. Click Change
|
##### Process Hacker <sub>[download](https://go.enderman.ch/process-hacker)</sub>
|
||||||
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
|
##### Winaero Tweaker <sub>[download](https://go.enderman.ch/winaero)</sub>
|
||||||
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.
|
[Sergey](https://go.enderman.ch/Dr0iS) makes an entirely valid point in his Winaero Tweaker:
|
||||||
|
|
||||||
|
> **Warning!!!**
|
||||||
|
>
|
||||||
|
> Running programs under TrustedInstaller privileges, especially a file manager app or Regedit,exe can be
|
||||||
|
> VERY risky and harmful for your OS. It is like a God mode in a first-person shooter game, where nothing can
|
||||||
|
> stop your actions, so if you execute a malware infected file as TrustedInstaller, it can cause damage to the
|
||||||
|
> Windows operating system. Use this mode only if you perfectly understand what you are doing and if there is
|
||||||
|
> no simpler way than running it as Trusted Installer.
|
||||||
|
|
||||||
|
This is exactly the reason I resort to setting permissions manually.
|
||||||
|
It's a bit more time-consuming, but it's safer.
|
||||||
|
|
||||||
|
### Setting permissions manually
|
||||||
|
The trusty old way to do it is to manually set permissions for the key.
|
||||||
|
Once you get the hang of it, it really isn't that difficult.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
##### General procedure
|
||||||
|
1. Navigate to the key you want to claim ownership of;
|
||||||
|
2. Open the «Properties» window;
|
||||||
|
3. Head over to the «Advanced» tab;
|
||||||
|
4. The current owner is shown at the top of the «Advanced Security Settings» window. Click «Change»;
|
||||||
|
5. Type out your username in the text area with the «Enter the object name to select (examples)» label;
|
||||||
|
6. Click «OK»;
|
||||||
|
7. Tick the «Replace owner on subcontainers and objects» checkbox;
|
||||||
|
8. Tick the «Replace all child object permission entries» checkbox;
|
||||||
|
9. Click «OK»;
|
||||||
|
10. Pick `Administrators (PC_NAME\Administrators)` in the group selector and then tick «Full Control» checkbox
|
||||||
|
in the «Permissions» window at the bottom;
|
||||||
|
11. You're done! Click «OK» and dismiss any warnings.
|
||||||
|
|
||||||
## Common registry paths
|
## Common registry paths
|
||||||
Here is a list of the most common registry paths.
|
Here is a list of the most common registry paths.
|
||||||
|
There are as many «sections» as there are extensions, but these are the most common ones.
|
||||||
|
They should cover most of your context menu.
|
||||||
|
|
||||||
#### Desktop Background — bottom rows
|
#### Desktop Background — bottom rows
|
||||||
`Computer\HKEY_CLASSES_ROOT\DesktopBackground`
|
`Computer\HKEY_CLASSES_ROOT\DesktopBackground`
|
||||||
|
@ -497,7 +615,7 @@ Here is a list of the most common registry paths.
|
||||||

|

|
||||||
|
|
||||||
#### Folder Item
|
#### Folder Item
|
||||||
This extends [Folder](#folder).
|
Extends [Folder](#folder).
|
||||||
`Computer\HKEY_CLASSES_ROOT\Folder`
|
`Computer\HKEY_CLASSES_ROOT\Folder`
|
||||||
|
|
||||||

|

|
||||||
|
@ -513,14 +631,15 @@ This extends [Folder](#folder).
|
||||||

|

|
||||||
|
|
||||||
#### Library Background
|
#### Library Background
|
||||||
|
Inherits from [Folder Background](#folder-background).
|
||||||
`Computer\HKEY_CLASSES_ROOT\LibraryFolder\background`
|
`Computer\HKEY_CLASSES_ROOT\LibraryFolder\background`
|
||||||
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.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
Despite the inheritance, it cannot parse the `%V` macro, which results in the following error.
|
||||||
|
Blame Microsoft for being as consistent as ever.
|
||||||
|
To my knowledge, there is no way to fix that.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### All Files
|
#### All Files
|
||||||
|
@ -529,12 +648,13 @@ To my knowledge, there is no way to fix that.
|
||||||

|

|
||||||
|
|
||||||
#### All Filesystem Objects
|
#### All Filesystem Objects
|
||||||
That's different from [All Files](#all-files) — it's a further generalization that includes system drives and symbols.
|
Different from [All Files](#all-files) — it's a further generalization that includes system drives,
|
||||||
|
symbolic links and other objects.
|
||||||
`Computer\HKEY_CLASSES_ROOT\AllFilesystemObjects`
|
`Computer\HKEY_CLASSES_ROOT\AllFilesystemObjects`
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### Type associations
|
#### File type associations
|
||||||
Text: `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\text`
|
Text: `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\text`
|
||||||
Images: `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\image`
|
Images: `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\image`
|
||||||
Documents: `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\document`
|
Documents: `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\document`
|
||||||
|
@ -542,36 +662,58 @@ Audio: `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\audio`
|
||||||
Video: `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\video`
|
Video: `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\video`
|
||||||
|
|
||||||
## Cleaning up the context menu
|
## Cleaning up the context menu
|
||||||
Most obnoxious default entries can be cleaned up using Winaero Tweaker.
|
The least fan-favorite default entries can be removed using Winaero Tweaker.
|
||||||
I suggest you use it to make the process way more bearable for yourself.
|
I suggest using it to make the whole cleanup slightly more bearable.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
#### Areas Winaero Tweaker is unable to reach to
|
### Some extra common areas
|
||||||
Remove «Open PowerShell window here»
|
The following are fairly common areas Winaero Tweaker doesn't cover.
|
||||||
Navigate to Computer\HKEY_CLASSES_ROOT\Directory\shell\Powershell
|
|
||||||
Unlock key (The Windows Context Menu > Dealing with locked keys)
|
::card
|
||||||
Create a new REG_SZ value ProgrammaticAccessOnly without any data
|
---
|
||||||
Navigate to Computer\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell
|
icon: idea
|
||||||
Unlock key
|
title: Tip
|
||||||
Create a new REG_SZ value ProgrammaticAccessOnly without any data
|
---
|
||||||
Remove «Open Linux shell here»
|
As a rule of thumb, whenever you're dealing with existing shell entries, I _really_ suggest
|
||||||
Navigate to Computer\HKEY_CLASSES_ROOT\Directory\shell\WSL
|
**searching for a way to manage them online** before resorting to any measures I elaborated on in this article.
|
||||||
Unlock key
|
Now if you can't find anything useful online, then anything works.
|
||||||
Delete WSL key altogether
|
::
|
||||||
Navigate to Computer\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell
|
|
||||||
Unlock key
|
#### Remove «Open PowerShell window here»
|
||||||
Create a new REG_SZ value ProgrammaticAccessOnly without any data
|
1. Navigate to `Computer\HKEY_CLASSES_ROOT\Directory\shell\Powershell`;
|
||||||
Edit the «Edit» text context menu command
|
2. [Unlock the key](#dealing-with-locked-keys);
|
||||||
Navigate to Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\text\shell\edit
|
3. Create a new `REG_SZ` value called `ProgrammaticAccessOnly` and leave it empty;
|
||||||
Make sure you've read the article thoroughly, then do anything you want with it.
|
4. Navigate to `Computer\HKEY_CLASSES_ROOT\Directory\Background\shell\Powershell`;
|
||||||
|
5. [Unlock the key](#dealing-with-locked-keys);
|
||||||
|
6. Create a new `REG_SZ` value called `ProgrammaticAccessOnly` and leave it empty.
|
||||||
|
|
||||||
|
#### Remove «Open Linux shell here»
|
||||||
|
1. Navigate to `Computer\HKEY_CLASSES_ROOT\Directory\shell\WSL`;
|
||||||
|
2. [Unlock the key](#dealing-with-locked-keys);
|
||||||
|
3. Remove the key;
|
||||||
|
4. Navigate to `Computer\HKEY_CLASSES_ROOT\Directory\Background\shell\WSL`;
|
||||||
|
5. [Unlock the key](#dealing-with-locked-keys);
|
||||||
|
6. Create a new `REG_SZ` value called `ProgrammaticAccessOnly` and leave it empty.
|
||||||
|
|
||||||
|
#### Edit the «Edit» text context menu command
|
||||||
|
1. Navigate to `Computer\HKEY_CLASSES_ROOT\SystemFileAssociations\text\shell\edit`;
|
||||||
|
2. Replace all mentions of the previous editor in default values with **Notepad++**
|
||||||
|
or any other text editor you prefer.
|
||||||
|
|
||||||
## Bottom line
|
## Bottom line
|
||||||
No matter how terrible the context menu API might be for Windows, there's still a way to customize it.
|
The context menu is a mess, and it's not going to change.
|
||||||
Hope you found the tricks useful!
|
It's a part of Windows that's been around for a long time and has been left behind by Microsoft.
|
||||||
|
Shell entries are like differential equations — there's never a general solution.
|
||||||
|
They're a pain to learn, but the acquired skill is worth it.
|
||||||
|
|
||||||
If you've spotted a mistake (be it orthographical, factual or any other)
|
No matter how terrible the context menu API is in Windows, there's still a way to customize it to your liking.
|
||||||
in this article or want to add some important details or share any important knowledge on the subject,
|
Hopefully you learned something new from this article
|
||||||
please contact me on Twitter: @endermanch or hit me up on e-mail: blogs@enderman.ch
|
and are now able to make your context menu **a bit** more bearable.
|
||||||
|
|
||||||
|
**Spotted a mistake?**
|
||||||
|
Want to add important details I missed or share extra knowledge on the subject?
|
||||||
|
Tag me on [Twitter](https://go.enderman.ch/twitter) or [e-mail me](mailto:contact@enderman.ch)!
|
||||||
|
All contributions welcome. 😉
|
||||||
|
|
|
@ -59,34 +59,38 @@ export default defineNuxtConfig({
|
||||||
'nuxt-link-checker',
|
'nuxt-link-checker',
|
||||||
],
|
],
|
||||||
content: {
|
content: {
|
||||||
|
documentDriven: true,
|
||||||
markdown: {
|
markdown: {
|
||||||
remarkPlugins: ['remark-reading-time'],
|
remarkPlugins: ['remark-reading-time'],
|
||||||
},
|
},
|
||||||
highlight: {
|
highlight: {
|
||||||
theme: {
|
theme: 'github-dark',
|
||||||
// Default theme (same as single string)
|
|
||||||
default: 'github-light',
|
|
||||||
// Theme used if `html.dark`
|
|
||||||
dark: 'github-dark',
|
|
||||||
// Theme used if `html.sepia`
|
|
||||||
sepia: 'monokai',
|
|
||||||
},
|
|
||||||
langs: [
|
langs: [
|
||||||
|
'shell',
|
||||||
|
'batch',
|
||||||
|
'vb',
|
||||||
|
'ini',
|
||||||
|
'asm',
|
||||||
'c',
|
'c',
|
||||||
'cpp',
|
'cpp',
|
||||||
'java',
|
'java',
|
||||||
|
'python',
|
||||||
'csv',
|
'csv',
|
||||||
'xml',
|
'xml',
|
||||||
'json',
|
'json',
|
||||||
'js',
|
'yaml',
|
||||||
'ts',
|
|
||||||
'html',
|
'html',
|
||||||
'css',
|
'css',
|
||||||
|
'sass',
|
||||||
|
'php',
|
||||||
|
'js',
|
||||||
|
'ts',
|
||||||
'vue',
|
'vue',
|
||||||
'shell',
|
|
||||||
'mdc',
|
|
||||||
'md',
|
'md',
|
||||||
'yaml',
|
'mdc',
|
||||||
|
'pascal',
|
||||||
|
'lisp',
|
||||||
|
'sql',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,24 +1,111 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { formatDate } from 'date-fns'
|
||||||
const { slug } = useRoute().params
|
const { slug } = useRoute().params
|
||||||
|
|
||||||
|
const config = useAppConfig()
|
||||||
|
const content = useContent()
|
||||||
|
|
||||||
|
const meta = {
|
||||||
|
title: `${content.page.title}`,
|
||||||
|
description: content.page.description,
|
||||||
|
image: content.page.thumbnail,
|
||||||
|
url: `${config.url}/blog`,
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOgImage()
|
||||||
|
|
||||||
|
// Hydrate the rendered items.
|
||||||
|
onMounted(() => {
|
||||||
|
document.querySelectorAll('pre').forEach((pre) => {
|
||||||
|
const icon = document.createElement('iconify-icon')
|
||||||
|
|
||||||
|
icon.setAttribute('icon', 'mdi:content-copy')
|
||||||
|
icon.setAttribute('inline', 'true')
|
||||||
|
|
||||||
|
icon.classList.add('button')
|
||||||
|
|
||||||
|
pre.appendChild(icon)
|
||||||
|
})
|
||||||
|
|
||||||
|
document
|
||||||
|
.querySelectorAll('code:not(pre *), pre > iconify-icon.button')
|
||||||
|
.forEach((code) => {
|
||||||
|
if (code instanceof HTMLElement) {
|
||||||
|
code.onclick = () => {
|
||||||
|
const area = document.createElement('textarea')
|
||||||
|
|
||||||
|
area.textContent =
|
||||||
|
code.nodeName && code.nodeName.toLowerCase() === 'code'
|
||||||
|
? code.textContent
|
||||||
|
: code.parentElement!.textContent
|
||||||
|
|
||||||
|
// It's necessary to create the textarea element every time you copy to get access to the select() method.
|
||||||
|
area.setSelectionRange(0, 99999) // An iOS gotcha.
|
||||||
|
area.select()
|
||||||
|
|
||||||
|
// Copy the text inside the textarea.
|
||||||
|
navigator.clipboard.writeText(area.value)
|
||||||
|
|
||||||
|
// TODO: Alert the user text has been successfully copied.
|
||||||
|
|
||||||
|
// Remove the textarea element.
|
||||||
|
area.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
document.querySelectorAll('code').forEach((code) => {
|
||||||
|
code.onclick = null
|
||||||
|
})
|
||||||
|
|
||||||
|
document.querySelectorAll('pre').forEach((pre) => {
|
||||||
|
pre.querySelector('.clipboard')?.remove()
|
||||||
|
})
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<article
|
<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"
|
class="post 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}`">
|
<ContentDoc :path="`/blog/${slug}`">
|
||||||
<template #default="{ doc }">
|
<template #default="{ doc }">
|
||||||
<div class="post-preamble">
|
<div
|
||||||
<h3 class="alex">{{ doc.title }}</h3>
|
class="post-preamble"
|
||||||
<img
|
:style="{ backgroundImage: 'url(' + doc.thumbnail + ')' }"
|
||||||
draggable="false"
|
>
|
||||||
:src="doc.thumbnail"
|
<div class="p-2">
|
||||||
:alt="doc.title"
|
<h3 class="alex">{{ doc.title }}</h3>
|
||||||
class="post-preamble-thumb rounded-4"
|
<div class="d-flex flex-row row-gap-0 column-gap-2 flex-wrap">
|
||||||
/>
|
<div class="d-flex flex-row gap-1 align-items-center">
|
||||||
|
<iconify-icon icon="mdi:calendar" />
|
||||||
|
<strong class="nobr font-small">
|
||||||
|
{{ formatDate(doc.created, 'LLLL do, y – HH:mm') }}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-row gap-1 align-items-center">
|
||||||
|
<iconify-icon icon="mdi:clock-outline" />
|
||||||
|
<strong class="nobr font-small">
|
||||||
|
{{ doc.readingTime.text.split(' ')[0] + ' minutes to read' }}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<NuxtLink
|
||||||
|
:href="`https://twitter.com/share?url=${config.url}/blog/${slug}&text=${doc.title}&hashtags=${doc.tags.slice(0, 3).join(',').replace(/ /g, '')}`"
|
||||||
|
target="_blank"
|
||||||
|
class="link post-preamble-share px-2 py-1"
|
||||||
|
>
|
||||||
|
<iconify-icon icon="mdi:twitter"></iconify-icon>
|
||||||
|
</NuxtLink>
|
||||||
|
<NuxtLink to="/blog" class="link post-preamble-control px-2 py-1">
|
||||||
|
<iconify-icon icon="icon-park-solid:back"></iconify-icon>
|
||||||
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="post-content">
|
<div class="post-content">
|
||||||
|
<Separator class="mt-0" />
|
||||||
<ContentRenderer :value="doc" />
|
<ContentRenderer :value="doc" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,42 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { formatDate } from 'date-fns'
|
import { formatDate } from 'date-fns'
|
||||||
|
|
||||||
|
const config = useAppConfig()
|
||||||
|
const meta = {
|
||||||
|
title: 'The Enderchest',
|
||||||
|
description:
|
||||||
|
'A blog about software development, scientific research and Windows quirks.',
|
||||||
|
image: `${config.url}/images/chest.png`,
|
||||||
|
url: `${config.url}/blog`,
|
||||||
|
}
|
||||||
|
|
||||||
|
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: 'The Enderchest',
|
||||||
|
htmlAttrs: {
|
||||||
|
lang: config.locale || 'en',
|
||||||
|
},
|
||||||
|
link: [
|
||||||
|
{
|
||||||
|
rel: 'icon',
|
||||||
|
type: 'image/x-icon',
|
||||||
|
href: '/favicon.ico',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 74 KiB |
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
Binary file not shown.
After Width: | Height: | Size: 83 KiB |
Binary file not shown.
After Width: | Height: | Size: 4.5 KiB |
Loading…
Reference in New Issue