mirror of
https://github.com/kevinveenbirkenbach/homepage.veen.world.git
synced 2025-06-29 16:22:01 +02:00
Compare commits
9 Commits
ae6eb6d802
...
1629d9728d
Author | SHA1 | Date | |
---|---|---|---|
1629d9728d | |||
268c2eb452 | |||
cdfefe5a63 | |||
e2aee76f54 | |||
35815ce5aa | |||
166055422e | |||
96ab80eaf2 | |||
1fc9176000 | |||
f7282eba73 |
280
app/config.yaml
280
app/config.yaml
@ -1,88 +1,4 @@
|
|||||||
---
|
---
|
||||||
accounts:
|
|
||||||
name: Accounts
|
|
||||||
description: My Online Accounts
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-users
|
|
||||||
subitems:
|
|
||||||
- name: Meta
|
|
||||||
description: Social and developer networks
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-meta
|
|
||||||
url:
|
|
||||||
subitems:
|
|
||||||
- name: Instagram
|
|
||||||
description: Follow me on Instagram
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-instagram
|
|
||||||
url: https://www.instagram.com/kevinveenbirkenbach/
|
|
||||||
identifier: kevinveenbirkenbach
|
|
||||||
warning: Using software and platforms from the Meta corporation (e.g., Facebook, Instagram, WhatsApp) may compromise your data privacy and digital freedom due to centralized control, extensive data collection practices, and inconsistent moderation policies. These platforms often fail to adequately address harmful content, misinformation, and abuse.
|
|
||||||
- name: Facebook
|
|
||||||
description: Like my Facebook page
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-facebook
|
|
||||||
url: https://www.facebook.com/kevinveenbirkenbach
|
|
||||||
- link: navigation.header.contact.messenger
|
|
||||||
- name: Carreer Profiles
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-user-tie
|
|
||||||
subitems:
|
|
||||||
- name: XING
|
|
||||||
description: Visit my XING profile
|
|
||||||
icon:
|
|
||||||
class: bi bi-building
|
|
||||||
url: https://www.xing.com/profile/Kevin_VeenBirkenbach
|
|
||||||
|
|
||||||
- name: LinkedIn
|
|
||||||
description: Connect on LinkedIn
|
|
||||||
icon:
|
|
||||||
class: bi bi-linkedin
|
|
||||||
url: https://www.linkedin.com/in/kevinveenbirkenbach
|
|
||||||
|
|
||||||
- name: Sports
|
|
||||||
description: My sport activities
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-running
|
|
||||||
url:
|
|
||||||
subitems:
|
|
||||||
- name: Garmin
|
|
||||||
description: My Garmin activities
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-person-running
|
|
||||||
url: https://s.veen.world/garmin
|
|
||||||
|
|
||||||
- name: Eversports
|
|
||||||
description: My Eversports sessions
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-dumbbell
|
|
||||||
url: https://s.veen.world/eversports
|
|
||||||
|
|
||||||
- name: Duolingo
|
|
||||||
description: Learn with me on Duolingo
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-language
|
|
||||||
url: https://www.duolingo.com/profile/kevinbirkenbach
|
|
||||||
|
|
||||||
- name: Spotify
|
|
||||||
description: Listen to my playlists
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-spotify
|
|
||||||
url: https://open.spotify.com/user/31vebfzbjf3p7oualis76qfpr5ty
|
|
||||||
|
|
||||||
- name: Patreon
|
|
||||||
description: Support me on Patreon
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-patreon
|
|
||||||
url: https://patreon.com/kevinveenbirkenbach
|
|
||||||
|
|
||||||
- name: Discourse
|
|
||||||
description: Follow me on Discourse
|
|
||||||
icon:
|
|
||||||
class: fa-brands fa-discourse
|
|
||||||
url: https://forum.veen.world/u/kevinveenbirkenbach
|
|
||||||
|
|
||||||
|
|
||||||
cards:
|
cards:
|
||||||
- icon:
|
- icon:
|
||||||
source: https://cloud.veen.world/s/logo_agile_coach_512x512/download
|
source: https://cloud.veen.world/s/logo_agile_coach_512x512/download
|
||||||
@ -159,7 +75,6 @@ cards:
|
|||||||
I deliver expert consulting services. Currently training for my Private Pilot
|
I deliver expert consulting services. Currently training for my Private Pilot
|
||||||
License, I specialize in guiding clients through aviation regulations, safety
|
License, I specialize in guiding clients through aviation regulations, safety
|
||||||
standards, and operational efficiency.
|
standards, and operational efficiency.
|
||||||
url:
|
|
||||||
link_text: Website under construction
|
link_text: Website under construction
|
||||||
- icon:
|
- icon:
|
||||||
source: https://cloud.veen.world/s/logo_hunter_512x512/download
|
source: https://cloud.veen.world/s/logo_hunter_512x512/download
|
||||||
@ -168,7 +83,6 @@ cards:
|
|||||||
walks, survival trainings, and photo expeditions, merging ecological knowledge
|
walks, survival trainings, and photo expeditions, merging ecological knowledge
|
||||||
with nature respect. My goal is to foster sustainable conservation and enhance
|
with nature respect. My goal is to foster sustainable conservation and enhance
|
||||||
appreciation for the natural world through responsible practices.
|
appreciation for the natural world through responsible practices.
|
||||||
url:
|
|
||||||
link_text: Website under construction
|
link_text: Website under construction
|
||||||
- icon:
|
- icon:
|
||||||
source: https://cloud.veen.world/s/logo_diver_512x512/download
|
source: https://cloud.veen.world/s/logo_diver_512x512/download
|
||||||
@ -177,7 +91,6 @@ cards:
|
|||||||
diving instruction, underwater photography, and guided dive tours. My experience
|
diving instruction, underwater photography, and guided dive tours. My experience
|
||||||
ensures safe and enriching underwater adventures, highlighting marine conservation
|
ensures safe and enriching underwater adventures, highlighting marine conservation
|
||||||
and the wonders of aquatic ecosystems.
|
and the wonders of aquatic ecosystems.
|
||||||
url:
|
|
||||||
link_text: Website under construction
|
link_text: Website under construction
|
||||||
- icon:
|
- icon:
|
||||||
source: https://cloud.veen.world/s/logo_massage_therapist_512x512/download
|
source: https://cloud.veen.world/s/logo_massage_therapist_512x512/download
|
||||||
@ -185,8 +98,84 @@ cards:
|
|||||||
text: Certified in Tantra Massage, I offer unique full-body rituals to awaken senses
|
text: Certified in Tantra Massage, I offer unique full-body rituals to awaken senses
|
||||||
and harmonize body and mind. My sessions, a blend of ancient Tantra and modern
|
and harmonize body and mind. My sessions, a blend of ancient Tantra and modern
|
||||||
relaxation, focus on energy flow, personal growth, and spiritual awakening.
|
relaxation, focus on energy flow, personal growth, and spiritual awakening.
|
||||||
url:
|
|
||||||
link_text: Website under construction
|
link_text: Website under construction
|
||||||
|
|
||||||
|
accounts:
|
||||||
|
name: Accounts
|
||||||
|
description: My Accounts
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-user-group
|
||||||
|
subitems:
|
||||||
|
- name: Meta
|
||||||
|
description: Social and developer networks
|
||||||
|
icon:
|
||||||
|
class: fa-brands fa-meta
|
||||||
|
subitems:
|
||||||
|
- name: Instagram
|
||||||
|
description: Follow me on Instagram
|
||||||
|
icon:
|
||||||
|
class: fa-brands fa-instagram
|
||||||
|
url: https://www.instagram.com/kevinveenbirkenbach/
|
||||||
|
identifier: kevinveenbirkenbach
|
||||||
|
link: navigation.header.contact.whatsapp.warning
|
||||||
|
- name: Facebook
|
||||||
|
description: Like my Facebook page
|
||||||
|
icon:
|
||||||
|
class: fa-brands fa-facebook
|
||||||
|
url: https://www.facebook.com/kevinveenbirkenbach
|
||||||
|
- name: Messengers
|
||||||
|
description: Messenger Applications
|
||||||
|
icon:
|
||||||
|
class: fas fa-comments
|
||||||
|
subitems:
|
||||||
|
- link: navigation.header.contact.whatsapp
|
||||||
|
- link: navigation.header.contact.signal
|
||||||
|
- link: navigation.header.contact.telegram
|
||||||
|
- name: Carreer Profiles
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-user-tie
|
||||||
|
subitems:
|
||||||
|
- name: XING
|
||||||
|
description: Visit my XING profile
|
||||||
|
icon:
|
||||||
|
class: bi bi-building
|
||||||
|
url: https://www.xing.com/profile/Kevin_VeenBirkenbach
|
||||||
|
- name: LinkedIn
|
||||||
|
description: Connect on LinkedIn
|
||||||
|
icon:
|
||||||
|
class: bi bi-linkedin
|
||||||
|
url: https://www.linkedin.com/in/kevinveenbirkenbach
|
||||||
|
- name: Sports
|
||||||
|
description: My sport activities
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-running
|
||||||
|
subitems:
|
||||||
|
- name: Garmin
|
||||||
|
description: My Garmin activities
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-person-running
|
||||||
|
url: https://s.veen.world/garmin
|
||||||
|
- name: Eversports
|
||||||
|
description: My Eversports sessions
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-dumbbell
|
||||||
|
url: https://s.veen.world/eversports
|
||||||
|
- name: Duolingo
|
||||||
|
description: Learn with me on Duolingo
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-language
|
||||||
|
url: https://www.duolingo.com/profile/kevinbirkenbach
|
||||||
|
- name: Spotify
|
||||||
|
description: Listen to my playlists
|
||||||
|
icon:
|
||||||
|
class: fa-brands fa-spotify
|
||||||
|
url: https://open.spotify.com/user/31vebfzbjf3p7oualis76qfpr5ty
|
||||||
|
- name: Patreon
|
||||||
|
description: Support me on Patreon
|
||||||
|
icon:
|
||||||
|
class: fa-brands fa-patreon
|
||||||
|
url: https://patreon.com/kevinveenbirkenbach
|
||||||
|
|
||||||
company:
|
company:
|
||||||
titel: Kevin Veen-Birkenbach
|
titel: Kevin Veen-Birkenbach
|
||||||
subtitel: Consulting and Coaching Solutions
|
subtitel: Consulting and Coaching Solutions
|
||||||
@ -200,6 +189,7 @@ company:
|
|||||||
city: Berlin
|
city: Berlin
|
||||||
country: Germany
|
country: Germany
|
||||||
imprint_url: https://s.veen.world/imprint
|
imprint_url: https://s.veen.world/imprint
|
||||||
|
|
||||||
navigation:
|
navigation:
|
||||||
header:
|
header:
|
||||||
- name: Microblog
|
- name: Microblog
|
||||||
@ -207,25 +197,21 @@ navigation:
|
|||||||
icon:
|
icon:
|
||||||
class: fa-brands fa-mastodon
|
class: fa-brands fa-mastodon
|
||||||
url: https://microblog.veen.world/@kevinveenbirkenbach
|
url: https://microblog.veen.world/@kevinveenbirkenbach
|
||||||
|
|
||||||
- name: Pictures
|
- name: Pictures
|
||||||
description: View my photo gallery
|
description: View my photo gallery
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-camera
|
class: fa-solid fa-camera
|
||||||
url: https://picture.veen.world/kevinveenbirkenbach
|
url: https://picture.veen.world/kevinveenbirkenbach
|
||||||
|
|
||||||
- name: Videos
|
- name: Videos
|
||||||
description: Watch my videos
|
description: Watch my videos
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-video
|
class: fa-solid fa-video
|
||||||
url: https://video.veen.world/a/kevinveenbirkenbach
|
url: https://video.veen.world/a/kevinveenbirkenbach
|
||||||
|
|
||||||
- name: Blog
|
- name: Blog
|
||||||
description: Read my blog
|
description: Read my blog
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-blog
|
class: fa-solid fa-blog
|
||||||
url: https://blog.veen.world
|
url: https://blog.veen.world
|
||||||
|
|
||||||
- name: Code
|
- name: Code
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-laptop-code
|
class: fa-solid fa-laptop-code
|
||||||
@ -236,13 +222,11 @@ navigation:
|
|||||||
icon:
|
icon:
|
||||||
class: bi bi-github
|
class: bi bi-github
|
||||||
url: https://github.com/kevinveenbirkenbach
|
url: https://github.com/kevinveenbirkenbach
|
||||||
|
|
||||||
- name: Gitea
|
- name: Gitea
|
||||||
description: Explore my code repositories
|
description: Explore my code repositories
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-code
|
class: fa-solid fa-code
|
||||||
url: https://git.veen.world/kevinveenbirkenbach
|
url: https://git.veen.world/kevinveenbirkenbach
|
||||||
|
|
||||||
- name: Contact
|
- name: Contact
|
||||||
description: Get in touch
|
description: Get in touch
|
||||||
icon:
|
icon:
|
||||||
@ -255,7 +239,25 @@ navigation:
|
|||||||
url: mailto:kevin@veen.world
|
url: mailto:kevin@veen.world
|
||||||
identifier: kevin@veen.world
|
identifier: kevin@veen.world
|
||||||
alternatives:
|
alternatives:
|
||||||
- link: navigation.header.contact.messenger.matrix
|
#- link: navigation.header.contact.matrix
|
||||||
|
- name: Matrix
|
||||||
|
description: Chat with me on Matrix
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-cubes
|
||||||
|
identifier: "@kevinveenbirkenbach:veen.world"
|
||||||
|
info: |
|
||||||
|
#### Why Use Matrix?
|
||||||
|
Matrix is a secure, decentralized communication platform that ensures privacy and control over your data. Learn more about [Matrix](https://matrix.org/).
|
||||||
|
|
||||||
|
#### Privacy and Security
|
||||||
|
End-to-end encryption keeps your conversations private and secure.
|
||||||
|
|
||||||
|
#### Decentralized and Open
|
||||||
|
Matrix's federated network means you can host your own server or use any provider while staying connected.
|
||||||
|
|
||||||
|
#### A Movement for Digital Freedom
|
||||||
|
By using Matrix, you support open, transparent, and secure communication.
|
||||||
|
|
||||||
- name: Mobile
|
- name: Mobile
|
||||||
description: Call me
|
description: Call me
|
||||||
icon:
|
icon:
|
||||||
@ -281,28 +283,7 @@ navigation:
|
|||||||
|
|
||||||
#### Stand for Security
|
#### Stand for Security
|
||||||
Using PGP is more than a tool—it's a statement about valuing freedom, privacy, and the security of digital communication. Explore the principles of secure communication with [privacy guides](https://privacyguides.org/).
|
Using PGP is more than a tool—it's a statement about valuing freedom, privacy, and the security of digital communication. Explore the principles of secure communication with [privacy guides](https://privacyguides.org/).
|
||||||
- name: Messenger
|
|
||||||
description: Social and developer networks
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-comments
|
|
||||||
subitems:
|
|
||||||
- name: Matrix
|
|
||||||
description: Chat with me on Matrix
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-cubes
|
|
||||||
identifier: "@kevinveenbirkenbach:veen.world"
|
|
||||||
info: |
|
|
||||||
#### Why Use Matrix?
|
|
||||||
Matrix is a secure, decentralized communication platform that ensures privacy and control over your data. Learn more about [Matrix](https://matrix.org/).
|
|
||||||
|
|
||||||
#### Privacy and Security
|
|
||||||
End-to-end encryption keeps your conversations private and secure.
|
|
||||||
|
|
||||||
#### Decentralized and Open
|
|
||||||
Matrix's federated network means you can host your own server or use any provider while staying connected.
|
|
||||||
|
|
||||||
#### A Movement for Digital Freedom
|
|
||||||
By using Matrix, you support open, transparent, and secure communication.
|
|
||||||
- name: Signal
|
- name: Signal
|
||||||
description: Message me on Signal
|
description: Message me on Signal
|
||||||
icon:
|
icon:
|
||||||
@ -310,7 +291,7 @@ navigation:
|
|||||||
identifier: "+491781798023"
|
identifier: "+491781798023"
|
||||||
warning: Signal is not hosted by me!
|
warning: Signal is not hosted by me!
|
||||||
alternatives:
|
alternatives:
|
||||||
- link: navigation.header.contact.messenger.matrix
|
#- link: navigation.header.contact.matrix
|
||||||
- name: Telegram
|
- name: Telegram
|
||||||
description: Message me on Telegram
|
description: Message me on Telegram
|
||||||
icon:
|
icon:
|
||||||
@ -320,29 +301,22 @@ navigation:
|
|||||||
identifier: kevinveenbirkenbach
|
identifier: kevinveenbirkenbach
|
||||||
warning: Telegram is not hosted by me!
|
warning: Telegram is not hosted by me!
|
||||||
alternatives:
|
alternatives:
|
||||||
- link: navigation.header.contact.messenger.matrix
|
#- link: navigation.header.contact.matrix
|
||||||
- name: WhatsApp
|
- name: WhatsApp
|
||||||
description: Chat with me on WhatsApp
|
description: Chat with me on WhatsApp
|
||||||
icon:
|
icon:
|
||||||
class: fa-brands fa-whatsapp
|
class: fa-brands fa-whatsapp
|
||||||
url: https://wa.me/491781798023
|
url: https://wa.me/491781798023
|
||||||
identifier: "+491781798023"
|
identifier: "+491781798023"
|
||||||
info: Consider using decentralized and privacy-respecting alternatives to maintain control over your data, improve security, and foster healthier online interactions.
|
|
||||||
alternatives:
|
alternatives:
|
||||||
- link: navigation.header.contact.messenger.matrix
|
#- link: navigation.header.contact.matrix
|
||||||
- link: navigation.header.contact.messenger.signal
|
warning: |
|
||||||
- link: navigation.header.contact.messenger.telegram
|
Using software and platforms from the Meta corporation (e.g., Facebook, Instagram, WhatsApp) may compromise your data privacy and digital freedom due to centralized control, extensive data collection practices, and inconsistent moderation policies. These platforms often fail to adequately address harmful content, misinformation, and abuse.
|
||||||
|
|
||||||
footer:
|
footer:
|
||||||
- link: accounts
|
- link: accounts
|
||||||
- name: Solution Hub
|
|
||||||
description: Curated collection of self hosted tools
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-network-wired
|
|
||||||
url:
|
|
||||||
subitems:
|
|
||||||
- name: Community
|
- name: Community
|
||||||
description: Tools to manage the community
|
description: My presence in the Fediverse
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-users
|
class: fa-solid fa-users
|
||||||
subitems:
|
subitems:
|
||||||
@ -350,83 +324,67 @@ navigation:
|
|||||||
description: Join the discussion
|
description: Join the discussion
|
||||||
icon:
|
icon:
|
||||||
class: fa-brands fa-discourse
|
class: fa-brands fa-discourse
|
||||||
url: https://forum.veen.world/
|
url: https://forum.veen.world/u/kevinveenbirkenbach
|
||||||
- name: Learning Platform
|
|
||||||
description: Learn with my academy
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-graduation-cap
|
|
||||||
url: https://academy.veen.world/
|
|
||||||
- name: Newsletter
|
- name: Newsletter
|
||||||
description: Subscribe to my newsletter
|
description: Subscribe to my newsletter
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-envelope-open-text
|
class: fa-solid fa-envelope-open-text
|
||||||
url: https://newsletter.veen.world/subscription/form
|
url: https://newsletter.veen.world/subscription/form
|
||||||
- name: Project Management
|
- name: Work Hub
|
||||||
description: Project Management Tools
|
description: Curated collection of self hosted tools for work, organization, and learning.
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-chart-line
|
class: fa-solid fa-toolbox
|
||||||
subitems:
|
subitems:
|
||||||
- name: Open Project
|
- name: Open Project
|
||||||
description: Explore my projects
|
description: Explore my projects
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-tasks
|
class: fa-solid fa-chart-line
|
||||||
url: https://project.veen.world/
|
url: https://project.veen.world/
|
||||||
|
|
||||||
- name: Taiga
|
- name: Taiga
|
||||||
description: View my Kanban board
|
description: View my Kanban board
|
||||||
icon:
|
icon:
|
||||||
class: bi bi-clipboard2-check-fill
|
class: bi bi-clipboard2-check-fill
|
||||||
url: https://kanban.veen.world/
|
url: https://kanban.veen.world/
|
||||||
|
|
||||||
- name: Matomo
|
- name: Matomo
|
||||||
description: Analyze with Matomo
|
description: Analyze with Matomo
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-chart-simple
|
class: fa-solid fa-chart-simple
|
||||||
url: https://matomo.veen.world/
|
url: https://matomo.veen.world/
|
||||||
|
|
||||||
- name: Baserow
|
- name: Baserow
|
||||||
description: Organize with Baserow
|
description: Organize with Baserow
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-table
|
class: fa-solid fa-table
|
||||||
url: https://baserow.veen.world/
|
url: https://baserow.veen.world/
|
||||||
- name: Communication
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-comments
|
|
||||||
subitems:
|
|
||||||
- name: Elements
|
- name: Elements
|
||||||
description: Chat with me
|
description: Chat with me
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-comment
|
class: fa-solid fa-comment
|
||||||
url: https://element.veen.world/
|
url: https://element.veen.world/
|
||||||
|
|
||||||
- name: Big Blue Button
|
- name: Big Blue Button
|
||||||
description: Join live events
|
description: Join live events
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-video
|
class: fa-solid fa-video
|
||||||
url: https://meet.veen.world/
|
url: https://meet.veen.world/
|
||||||
|
|
||||||
- name: Mailu
|
- name: Mailu
|
||||||
description: Send me a mail
|
description: Send me a mail
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-envelope
|
class: fa-solid fa-envelope
|
||||||
url: https://mail.veen.world/
|
url: https://mail.veen.world/
|
||||||
|
- name: Moodel
|
||||||
|
description: Learn with my academy
|
||||||
|
icon:
|
||||||
|
class: fa-solid fa-graduation-cap
|
||||||
|
url: https://academy.veen.world/
|
||||||
- name: Yourls
|
- name: Yourls
|
||||||
description: Find my curated links
|
description: Find my curated links
|
||||||
icon:
|
icon:
|
||||||
class: bi bi-link
|
class: bi bi-link
|
||||||
url: https://s.veen.world/admin/
|
url: https://s.veen.world/admin/
|
||||||
|
|
||||||
- name: Nextcloud
|
- name: Nextcloud
|
||||||
description: Access my cloud storage
|
description: Access my cloud storage
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-cloud
|
class: fa-solid fa-cloud
|
||||||
url: https://cloud.veen.world/
|
url: https://cloud.veen.world/
|
||||||
|
|
||||||
- name: About
|
|
||||||
description: All information about me
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-user
|
|
||||||
subitems:
|
|
||||||
- name: Logbooks
|
- name: Logbooks
|
||||||
description: My activity logs
|
description: My activity logs
|
||||||
icon:
|
icon:
|
||||||
@ -437,41 +395,31 @@ navigation:
|
|||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-parachute-box
|
class: fa-solid fa-parachute-box
|
||||||
url: https://s.veen.world/skydiverlog
|
url: https://s.veen.world/skydiverlog
|
||||||
|
|
||||||
- name: Skipper
|
- name: Skipper
|
||||||
description: See my sailing records
|
description: See my sailing records
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-sailboat
|
class: fa-solid fa-sailboat
|
||||||
url: https://s.veen.world/meilenbuch
|
url: https://s.veen.world/meilenbuch
|
||||||
|
|
||||||
- name: Diver
|
- name: Diver
|
||||||
description: Check my diving logs
|
description: Check my diving logs
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-fish
|
class: fa-solid fa-fish
|
||||||
url: https://s.veen.world/diverlog
|
url: https://s.veen.world/diverlog
|
||||||
|
|
||||||
- name: Pilot
|
- name: Pilot
|
||||||
description: Review my flight logs
|
description: Review my flight logs
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-plane
|
class: fa-solid fa-plane
|
||||||
url: https://s.veen.world/pilotlog
|
url: https://s.veen.world/pilotlog
|
||||||
|
|
||||||
- name: Nature
|
- name: Nature
|
||||||
description: Explore my nature logs
|
description: Explore my nature logs
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-tree
|
class: fa-solid fa-tree
|
||||||
url: https://s.veen.world/naturejournal
|
url: https://s.veen.world/naturejournal
|
||||||
- name: Vita
|
- name: Vita
|
||||||
description: View my CV
|
description: View my CV and professional background
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-file-lines
|
class: fa-solid fa-file-lines
|
||||||
url: https://s.veen.world/lebenslauf
|
url: https://s.veen.world/lebenslauf
|
||||||
- name: Certificates
|
|
||||||
description: View my certifications and degrees
|
|
||||||
icon:
|
|
||||||
class: fa-solid fa-graduation-cap
|
|
||||||
url: https://s.veen.world/lebenslauf
|
|
||||||
|
|
||||||
- name: Imprint
|
- name: Imprint
|
||||||
icon:
|
icon:
|
||||||
class: fa-solid fa-scale-balanced
|
class: fa-solid fa-scale-balanced
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
/* General link styles */
|
|
||||||
a {
|
a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Header styles */
|
|
||||||
.header img {
|
.header img {
|
||||||
float: right;
|
float: right;
|
||||||
width: 100px;
|
width: 100px;
|
||||||
@ -14,39 +11,39 @@ a {
|
|||||||
.header h1 {
|
.header h1 {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Equal-height container using flexbox */
|
|
||||||
.equal-height {
|
.equal-height {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Card styles */
|
|
||||||
.navbar, .card {
|
|
||||||
flex: 1; /* Ensures cards fill the height of their container */
|
|
||||||
border-radius: 5px; /* Rounded corners */
|
|
||||||
border: 1px solid #ccc; /* Optional border color */
|
|
||||||
padding: 10px; /* Inner spacing */
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); /* Subtle shadow effect */
|
|
||||||
color: #000000 !important;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: center; /* Center content horizontally */
|
align-items: center; /* Zentriert die Inhalte horizontal */
|
||||||
text-align: center; /* Center text alignment */
|
text-align: center; /* Zentriert den Text */
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-icon {
|
.card-icon {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center; /* Center the icon horizontally */
|
justify-content: center; /* Zentriert das Icon horizontal */
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-text,
|
.card-text,
|
||||||
.card ul {
|
.card ul {
|
||||||
text-align: left; /* Align text to the left */
|
text-align: left; /* Stellt sicher, dass der Text linksbündig ist */
|
||||||
|
}
|
||||||
|
|
||||||
|
.card{
|
||||||
|
flex: 1; /* Stellt sicher, dass die Karten die ganze Höhe ihrer Container ausfüllen */
|
||||||
|
border-width: 3px;
|
||||||
|
/*border-color: #000000;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
h3.card-title{
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card .stretched-link{
|
||||||
|
font-size: 0.7em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-column{
|
.card-column{
|
||||||
@ -54,32 +51,21 @@ a {
|
|||||||
padding-bottom: 12px;
|
padding-bottom: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card .stretched-link {
|
h3.footer-title{
|
||||||
font-size: 0.7em;
|
|
||||||
}
|
|
||||||
|
|
||||||
h3.card-title {
|
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Footer styles */
|
|
||||||
.footer {
|
.footer {
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-size: 0.7em;
|
font-size: 0.7em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer p,
|
.footer p, h3{
|
||||||
.footer h3 {
|
margin: 0px;
|
||||||
margin: 0;
|
padding: 0px;
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
h3.footer-title {
|
|
||||||
font-size: 1.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dropdown menu styles */
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
}
|
}
|
||||||
@ -87,26 +73,40 @@ h3.footer-title {
|
|||||||
.dropdown-menu-footer {
|
.dropdown-menu-footer {
|
||||||
position: absolute !important;
|
position: absolute !important;
|
||||||
top: auto !important;
|
top: auto !important;
|
||||||
bottom: 100%; /* Positions the menu above the trigger */
|
bottom: 100%; /* Positioniert das Menü über dem Auslöser */
|
||||||
transform: translateY(-10px); /* Optional spacing for smoother appearance */
|
transform: translateY(-10px); /* Optional: Sanfter Abstand */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Dropdown submenu styles */
|
|
||||||
.dropdown-submenu {
|
.dropdown-submenu {
|
||||||
position: relative;
|
position: relative;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar, .card {
|
||||||
|
border-radius: 5px; /* Runde Ecken */
|
||||||
|
border: 1px solid #ccc; /* Optionale Rahmenfarbe */
|
||||||
|
padding: 10px; /* Optionaler Abstand innen */
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3); /* Optionale Schatteneffekte */
|
||||||
|
color: #000000 !important;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-nav {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
/* Stellt sicher, dass Submenüs korrekt positioniert sind */
|
||||||
|
.dropdown-submenu {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-submenu > .dropdown-menu {
|
.dropdown-submenu > .dropdown-menu {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 100%; /* Default position: open to the right */
|
left: 100%; /* Positioniert das Submenü rechts vom Hauptmenü */
|
||||||
margin-top: -1px;
|
margin-top: -1px;
|
||||||
z-index: 1050;
|
|
||||||
transition: opacity 0.3s ease-in-out; /* Smooth opacity transition */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle collapse behavior for dropdowns */
|
|
||||||
.dropdown-menu.collapse {
|
.dropdown-menu.collapse {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -115,7 +115,7 @@ h3.footer-title {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure submenus are hidden by default */
|
/* Standardmäßig sind die Submenüs ausgeblendet */
|
||||||
.dropdown-submenu .dropdown-menu {
|
.dropdown-submenu .dropdown-menu {
|
||||||
display: none;
|
display: none;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@ -125,19 +125,15 @@ h3.footer-title {
|
|||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show submenu on hover */
|
/* Beim Hover auf das Submenü-Element wird das Menü angezeigt */
|
||||||
.dropdown-submenu:hover > .dropdown-menu {
|
.dropdown-submenu:hover > .dropdown-menu {
|
||||||
display: block;
|
display: block;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
z-index: 1050;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Ensure submenu remains visible when hovered over */
|
/* Um sicherzustellen, dass es nicht sofort verschwindet */
|
||||||
.dropdown-submenu:hover > .dropdown-menu:hover {
|
.dropdown-submenu:hover > .dropdown-menu:hover {
|
||||||
display: block;
|
display: block;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle dynamic submenu positioning */
|
|
||||||
.dropdown-submenu > .dropdown-menu[style*="right: 100%"] {
|
|
||||||
left: auto; /* Override left position for leftward opening */
|
|
||||||
}
|
|
||||||
|
@ -9,19 +9,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
const menu = submenu.querySelector('.dropdown-menu');
|
const menu = submenu.querySelector('.dropdown-menu');
|
||||||
if (menu) {
|
if (menu) {
|
||||||
// Dynamische Positionierung
|
|
||||||
const rect = menu.getBoundingClientRect();
|
|
||||||
const viewportWidth = window.innerWidth;
|
|
||||||
|
|
||||||
// Überprüfen, ob Platz nach rechts ist, sonst nach links öffnen
|
|
||||||
if (rect.right > viewportWidth) {
|
|
||||||
menu.style.left = 'auto';
|
|
||||||
menu.style.right = '100%';
|
|
||||||
} else {
|
|
||||||
menu.style.left = '100%';
|
|
||||||
menu.style.right = 'auto';
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.style.display = 'block';
|
menu.style.display = 'block';
|
||||||
menu.style.opacity = '1';
|
menu.style.opacity = '1';
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,8 @@
|
|||||||
<hr />
|
<hr />
|
||||||
<h3 class="card-title">{{ card.title }}</h3>
|
<h3 class="card-title">{{ card.title }}</h3>
|
||||||
<p class="card-text">{{ card.text }}</p>
|
<p class="card-text">{{ card.text }}</p>
|
||||||
{% if card.url %}
|
{% if card.link %}
|
||||||
<a href="{{ card.url }}" class="mt-auto btn btn-light stretched-link" ><i class="fa-solid fa-globe"></i> {{ card.link_text }}</a>
|
<a href="{{ card.link }}" class="mt-auto btn btn-light stretched-link" ><i class="fa-solid fa-globe"></i> {{ card.link_text }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<i class="fa-solid fa-hourglass"></i> {{ card.link_text }}
|
<i class="fa-solid fa-hourglass"></i> {{ card.link_text }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -4,11 +4,7 @@
|
|||||||
{% if subitem.subitems %}
|
{% if subitem.subitems %}
|
||||||
<li class="dropdown-submenu position-relative">
|
<li class="dropdown-submenu position-relative">
|
||||||
<a class="dropdown-item dropdown-toggle" href="#" title="{{ subitem.description }}">
|
<a class="dropdown-item dropdown-toggle" href="#" title="{{ subitem.description }}">
|
||||||
{% if subitem.icon is defined and subitem.icon.class is defined %}
|
|
||||||
<i class="{{ subitem.icon.class }}"></i> {{ subitem.name }}
|
<i class="{{ subitem.icon.class }}"></i> {{ subitem.name }}
|
||||||
{% else %}
|
|
||||||
<p>Missing icon in subitem: {{ subitem }}</p>
|
|
||||||
{% endif %}
|
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
{{ render_subitems(subitem.subitems) }}
|
{{ render_subitems(subitem.subitems) }}
|
||||||
|
@ -1,101 +1,157 @@
|
|||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
|
import inspect
|
||||||
class ConfigurationResolver:
|
class ConfigurationResolver:
|
||||||
"""
|
|
||||||
A class to resolve `link` entries in a nested configuration structure.
|
|
||||||
Supports navigation through dictionaries, lists, and `subitems`.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
|
"""
|
||||||
|
Initializes the ConfigurationResolver with a configuration dictionary.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config (dict): The configuration to resolve links in.
|
||||||
|
"""
|
||||||
self.config = config
|
self.config = config
|
||||||
|
|
||||||
def resolve_links(self):
|
def resolve_links(self):
|
||||||
"""
|
"""
|
||||||
Resolves all `link` entries in the configuration.
|
Resolves all `link` entries in the configuration by replacing them with
|
||||||
|
the referenced configuration entry.
|
||||||
"""
|
"""
|
||||||
self._recursive_resolve(self.config, self.config)
|
self._recursive_resolve(self.config, self.config)
|
||||||
|
|
||||||
def _recursive_resolve(self, current_config, root_config):
|
def _recursive_resolve(self, current_config, root_config, path=""):
|
||||||
"""
|
"""
|
||||||
Recursively resolves `link` entries in the configuration.
|
Recursively traverses the configuration to resolve all `link` entries.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
current_config (dict or list): The current section of the configuration being processed.
|
||||||
|
root_config (dict): The root of the configuration to resolve links against.
|
||||||
|
path (str): The current path in the configuration for debugging purposes.
|
||||||
"""
|
"""
|
||||||
if isinstance(current_config, dict):
|
if isinstance(current_config, dict):
|
||||||
|
self._debug(current_config,path,inspect.currentframe().f_lineno)
|
||||||
|
# Traverse all key-value pairs in the dictionary
|
||||||
for key, value in list(current_config.items()):
|
for key, value in list(current_config.items()):
|
||||||
if key == "link":
|
if key == "subitems" and isinstance(value, list):
|
||||||
|
pass
|
||||||
|
#self._debug(value,path,inspect.currentframe().f_lineno)
|
||||||
|
#for index, item in enumerate(current_config[key]):
|
||||||
|
# #self._debug(value,path,inspect.currentframe().f_lineno)
|
||||||
|
# if "link" in item:
|
||||||
|
# self._debug(value,path,inspect.currentframe().f_lineno)
|
||||||
|
# self._recursive_resolve(item, root_config, path=f"{path}[{index}]")
|
||||||
|
|
||||||
|
elif key == "link":
|
||||||
|
# Found a `link` entry, attempt to resolve it
|
||||||
try:
|
try:
|
||||||
target = self._find_entry(root_config, value.lower(), True)
|
target = self._find_entry(root_config, value.lower().replace(" ", "_"))
|
||||||
if isinstance(target, list) and len(target) > 2:
|
|
||||||
target = self._find_entry(root_config, value.lower(), False)
|
if isinstance(target, dict):
|
||||||
|
self._debug(value,path,inspect.currentframe().f_lineno)
|
||||||
|
# Replace the current dictionary with the resolved dictionary
|
||||||
current_config.clear()
|
current_config.clear()
|
||||||
current_config.update(target)
|
current_config.update(target)
|
||||||
except Exception as e:
|
elif isinstance(target, str):
|
||||||
|
self._debug(value,path,inspect.currentframe().f_lineno)
|
||||||
|
# Replace the `link` entry with the resolved string
|
||||||
|
current_config[key] = target
|
||||||
|
else:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Error resolving link '{value}': {str(e)}. "
|
f"Expected a dictionary or string for link '{value}', got {type(target)}"
|
||||||
f"Current path: {key}, Current config: {current_config}"
|
)
|
||||||
|
except KeyError as e:
|
||||||
|
# Handle unresolved links
|
||||||
|
raise ValueError(
|
||||||
|
f"Key error while resolving link '{value}': {str(e)}. "
|
||||||
|
f"Current path: {path}, Current config: {current_config}"
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._recursive_resolve(value, root_config)
|
self._debug(value,path,inspect.currentframe().f_lineno)
|
||||||
|
# Recursively resolve non-`link` entries
|
||||||
|
self._recursive_resolve(value, root_config, path=f"{path}.{key}")
|
||||||
|
|
||||||
elif isinstance(current_config, list):
|
elif isinstance(current_config, list):
|
||||||
for item in current_config:
|
# Traverse all items in the list
|
||||||
self._recursive_resolve(item, root_config)
|
for index, item in enumerate(current_config):
|
||||||
|
self._recursive_resolve(item, root_config, path=f"{path}[{index}]")
|
||||||
|
|
||||||
def _get_subitems(self,current):
|
def _debug(self, value, path, line, condition="accounts"):
|
||||||
if isinstance(current, dict) and ("subitems" in current and current["subitems"]):
|
if condition in path:
|
||||||
current = current["subitems"]
|
print("LINE:" + str(line))
|
||||||
return current
|
print("PATH:" + path)
|
||||||
|
print("VALUE:")
|
||||||
|
pprint(value)
|
||||||
|
|
||||||
def _find_by_name(self,current, part):
|
def _find_entry(self, config, path):
|
||||||
return next(
|
"""
|
||||||
(item for item in current if isinstance(item, dict) and item.get("name", "").lower() == part),
|
Finds an entry in the configuration by navigating a dot-separated path.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config (dict or list): The configuration to search within.
|
||||||
|
path (str): The dot-separated path to the desired entry.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict or str: The resolved entry.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
KeyError: If the path cannot be resolved.
|
||||||
|
ValueError: If the resolved entry is not of the expected type.
|
||||||
|
"""
|
||||||
|
parts = path.split('.') # Split the path into segments
|
||||||
|
current = config
|
||||||
|
|
||||||
|
for part in parts:
|
||||||
|
part = part.replace(" ", "_") # Normalize part name
|
||||||
|
|
||||||
|
if isinstance(current, list):
|
||||||
|
# Search for a matching entry in a list
|
||||||
|
found = next(
|
||||||
|
(
|
||||||
|
item
|
||||||
|
for item in current
|
||||||
|
if isinstance(item, dict) and item.get("name", "").lower().replace(" ", "_") == part
|
||||||
|
),
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
|
if not found:
|
||||||
def _find_entry(self, config, path, subitems):
|
raise KeyError(
|
||||||
"""
|
|
||||||
Finds an entry in the configuration by a dot-separated path.
|
|
||||||
Supports both dictionaries and lists with `subitems` navigation.
|
|
||||||
"""
|
|
||||||
parts = path.split('.')
|
|
||||||
current = config
|
|
||||||
for part in parts:
|
|
||||||
if isinstance(current, list):
|
|
||||||
# Look for a matching name in the list
|
|
||||||
found = self._find_by_name(current,part)
|
|
||||||
if found:
|
|
||||||
print(
|
|
||||||
f"Matching entry for '{part}' in list. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
|
|
||||||
f"Current list: {current}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise ValueError(
|
|
||||||
f"No matching entry for '{part}' in list. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
|
f"No matching entry for '{part}' in list. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
|
||||||
f"Current list: {current}"
|
f"Current list: {current}"
|
||||||
)
|
)
|
||||||
current = found
|
current = found
|
||||||
|
|
||||||
elif isinstance(current, dict):
|
elif isinstance(current, dict):
|
||||||
# Case-insensitive dictionary lookup
|
# Search for a key match in a dictionary
|
||||||
key = next((k for k in current if k.lower() == part), None)
|
key = next((k for k in current if k.lower().replace(" ", "_") == part), None)
|
||||||
if key is None:
|
if key is None:
|
||||||
current = self._find_by_name(current["subitems"],part)
|
|
||||||
if not current:
|
|
||||||
raise KeyError(
|
raise KeyError(
|
||||||
f"Key '{part}' not found in dictionary. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
|
f"Key '{part}' not found in dictionary. Path so far: {' > '.join(parts[:parts.index(part)+1])}. "
|
||||||
f"Current dictionary: {current}"
|
f"Current dictionary: {current}"
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
current = current[key]
|
current = current[key]
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
# Invalid path segment
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"Invalid path segment '{part}'. Current type: {type(current)}. "
|
f"Invalid path segment '{part}'. Current type: {type(current)}. "
|
||||||
f"Path so far: {' > '.join(parts[:parts.index(part)+1])}"
|
f"Path so far: {' > '.join(parts[:parts.index(part)+1])}"
|
||||||
)
|
)
|
||||||
if subitems:
|
|
||||||
current = self._get_subitems(current)
|
# Stop navigating into `subitems` unless explicitly required by the path
|
||||||
|
if isinstance(current, dict) and "subitems" in current and isinstance(current["subitems"], list) and part != "subitems":
|
||||||
|
break # Stop navigation if `subitems` is not explicitly in the path
|
||||||
|
|
||||||
|
# Ensure the resolved target is a dictionary or string
|
||||||
|
if not isinstance(current, (dict, str)):
|
||||||
|
raise ValueError(
|
||||||
|
f"Expected a dictionary or string for path '{path}', got {type(current)}. Current value: {current}"
|
||||||
|
)
|
||||||
|
|
||||||
return current
|
return current
|
||||||
|
|
||||||
def get_config(self):
|
def get_config(self):
|
||||||
"""
|
"""
|
||||||
Returns the resolved configuration.
|
Returns the fully resolved configuration.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: The resolved configuration.
|
||||||
"""
|
"""
|
||||||
return self.config
|
return self.config
|
||||||
|
Loading…
x
Reference in New Issue
Block a user