initial commit
This commit is contained in:
commit
3c2fb32b3e
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
# build output
|
||||
dist/
|
||||
# generated types
|
||||
.astro/
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
.yarn/
|
||||
|
||||
# logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# jetbrains setting folder
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
modules/
|
||||
modules_old/
|
6
README.md
Normal file
6
README.md
Normal file
|
@ -0,0 +1,6 @@
|
|||
# Quickshell Docs
|
||||
|
||||
Documentation for [quickshell](https://git.outfoxxed.me/outfoxxed/quickshell)
|
||||
Hosted version at [quickshell.outfoxxed.me](https://quickshell.outfoxxed.me)
|
||||
|
||||
Frontend rewritten by [Xanazf](https://github.com/Xanazf)
|
41
astro.config.mjs
Normal file
41
astro.config.mjs
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { defineConfig } from "astro/config";
|
||||
import solidJs from "@astrojs/solid-js";
|
||||
import { remarkAlert } from "remark-github-blockquote-alert";
|
||||
import sectionize from "@hbsnow/rehype-sectionize";
|
||||
import mdx from "@astrojs/mdx";
|
||||
|
||||
import pagefind from "astro-pagefind";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [
|
||||
solidJs({
|
||||
devtools: true,
|
||||
}),
|
||||
mdx(),
|
||||
pagefind(),
|
||||
],
|
||||
markdown: {
|
||||
syntaxHighlight: "shiki",
|
||||
shikiConfig: {
|
||||
theme: "material-theme-ocean",
|
||||
wrap: true,
|
||||
},
|
||||
remarkPlugins: [
|
||||
[
|
||||
remarkAlert,
|
||||
{
|
||||
legacyTitle: true,
|
||||
},
|
||||
],
|
||||
],
|
||||
rehypePlugins: [
|
||||
[
|
||||
sectionize,
|
||||
{
|
||||
idPropertyName: "id",
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
});
|
87
biome.json
Normal file
87
biome.json
Normal file
|
@ -0,0 +1,87 @@
|
|||
{
|
||||
"$schema": "https://biomejs.dev/schemas/1.8.3/schema.json",
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"formatWithErrors": true,
|
||||
"indentStyle": "space",
|
||||
"indentWidth": 2,
|
||||
"lineEnding": "lf",
|
||||
"lineWidth": 66,
|
||||
"attributePosition": "multiline"
|
||||
},
|
||||
"organizeImports": {
|
||||
"enabled": true
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true,
|
||||
"style": {
|
||||
"noVar": "error",
|
||||
"useConst": "error"
|
||||
},
|
||||
"suspicious": {
|
||||
"noDoubleEquals": "error"
|
||||
},
|
||||
"correctness": {
|
||||
"useJsxKeyInIterable": "info"
|
||||
}
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"jsxQuoteStyle": "double",
|
||||
"quoteProperties": "asNeeded",
|
||||
"trailingCommas": "es5",
|
||||
"semicolons": "always",
|
||||
"arrowParentheses": "asNeeded",
|
||||
"bracketSpacing": true,
|
||||
"bracketSameLine": false,
|
||||
"quoteStyle": "double",
|
||||
"lineWidth": 66,
|
||||
"attributePosition": "multiline"
|
||||
}
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx"
|
||||
],
|
||||
"linter": {
|
||||
"rules": {
|
||||
"complexity": {
|
||||
"noBannedTypes": "off"
|
||||
},
|
||||
"correctness": {
|
||||
"noInvalidUseBeforeDeclaration": "off"
|
||||
},
|
||||
"style": {
|
||||
"noNonNullAssertion": "off"
|
||||
},
|
||||
"suspicious": {
|
||||
"noExplicitAny": "info",
|
||||
"noDuplicateClassMembers": "off",
|
||||
"noEmptyBlockStatements": "off"
|
||||
},
|
||||
"a11y": {
|
||||
"noSvgWithoutTitle": "info"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"include": [
|
||||
"*.astro"
|
||||
],
|
||||
"linter": {
|
||||
"rules": {
|
||||
"style": {
|
||||
"useConst": "off",
|
||||
"useImportType": "off"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
7728
package-lock.json
generated
Normal file
7728
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
37
package.json
Normal file
37
package.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "quickshell-docs",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro check && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ark-ui/solid": "^3.5.0",
|
||||
"@astrojs/check": "^0.9.3",
|
||||
"@astrojs/mdx": "^3.1.4",
|
||||
"@astrojs/solid-js": "^4.4.1",
|
||||
"@types/node": "^20.14.11",
|
||||
"astro": "^4.14.5",
|
||||
"astro-breadcrumbs": "^2.3.1",
|
||||
"astro-pagefind": "^1.6.0",
|
||||
"marked": "^14.1.0",
|
||||
"marked-alert": "^2.0.2",
|
||||
"node": "npm:22.7.0",
|
||||
"remark-github-blockquote-alert": "^1.2.1",
|
||||
"remark-parse": "^11.0.0",
|
||||
"solid-devtools": "^0.30.1",
|
||||
"solid-js": "^1.8.18",
|
||||
"typescript": "^5.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/ts-plugin": "^1.10.1",
|
||||
"@biomejs/biome": "^1.8.3",
|
||||
"@hbsnow/rehype-sectionize": "^1.0.7",
|
||||
"shiki": "^1.11.0"
|
||||
},
|
||||
"packageManager": "yarn@4.5.0"
|
||||
}
|
1
public/favicon.svg
Normal file
1
public/favicon.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 100 100"><rect width="100" height="100" rx="20" fill="#24ff70"></rect><path d="M50.41 76.74L41.38 76.74Q40.27 76.74 39.68 76.15Q39.10 75.56 38.75 75.15L38.75 75.15L36.41 72.05Q33.72 72.94 30.27 72.94L30.27 72.94Q23.92 72.94 19.26 70.87Q14.60 68.80 11.95 64.63Q9.29 60.45 9.08 54.17L9.08 54.17Q9.01 51.21 9.01 48.21Q9.01 45.20 9.08 42.24L9.08 42.24Q9.29 36.03 11.98 31.78Q14.67 27.54 19.36 25.40Q24.06 23.26 30.27 23.26L30.27 23.26Q36.54 23.26 41.24 25.40Q45.93 27.54 48.65 31.78Q51.38 36.03 51.52 42.24L51.52 42.24Q51.66 45.20 51.66 48.21Q51.66 51.21 51.52 54.17L51.52 54.17Q51.17 62.73 46.55 67.42L46.55 67.42L51.59 74.60Q51.66 74.67 51.72 74.87Q51.79 75.08 51.79 75.29L51.79 75.29Q51.86 75.84 51.45 76.29Q51.03 76.74 50.41 76.74L50.41 76.74ZM30.27 63.01L30.27 63.01Q33.92 63.01 36.23 60.80Q38.55 58.59 38.68 53.76L38.68 53.76Q38.82 50.72 38.82 48.03Q38.82 45.34 38.68 42.44L38.68 42.44Q38.61 39.20 37.51 37.17Q36.41 35.13 34.58 34.16Q32.75 33.20 30.27 33.20L30.27 33.20Q27.92 33.20 26.06 34.16Q24.19 35.13 23.12 37.17Q22.05 39.20 21.92 42.44L21.92 42.44Q21.85 45.34 21.85 48.03Q21.85 50.72 21.92 53.76L21.92 53.76Q22.12 58.59 24.40 60.80Q26.68 63.01 30.27 63.01ZM73.74 72.94L73.74 72.94Q69.25 72.94 66.11 71.94Q62.97 70.94 61.01 69.49Q59.04 68.04 58.07 66.49Q57.11 64.94 57.04 63.77L57.04 63.77Q56.97 63.01 57.52 62.52Q58.07 62.04 58.69 62.04L58.69 62.04L66.56 62.04Q66.77 62.04 66.94 62.11Q67.11 62.18 67.32 62.39L67.32 62.39Q68.22 62.87 69.11 63.59Q70.01 64.32 71.18 64.87Q72.36 65.42 74.01 65.42L74.01 65.42Q75.94 65.42 77.25 64.63Q78.57 63.83 78.57 62.39L78.57 62.39Q78.57 61.28 77.95 60.56Q77.32 59.83 75.43 59.14Q73.53 58.45 69.73 57.76L69.73 57.76Q66.15 56.93 63.45 55.59Q60.76 54.24 59.31 52.07Q57.87 49.90 57.87 46.79L57.87 46.79Q57.87 44.03 59.66 41.48Q61.45 38.93 64.90 37.30Q68.35 35.68 73.46 35.68L73.46 35.68Q77.39 35.68 80.36 36.61Q83.33 37.55 85.33 38.99Q87.33 40.44 88.36 41.96Q89.40 43.48 89.47 44.72L89.47 44.72Q89.54 45.41 89.05 45.93Q88.57 46.45 87.95 46.45L87.95 46.45L80.77 46.45Q80.50 46.45 80.22 46.34Q79.95 46.24 79.74 46.10L79.74 46.10Q78.91 45.69 78.08 45.00Q77.25 44.31 76.15 43.76Q75.05 43.20 73.39 43.20L73.39 43.20Q71.53 43.20 70.49 44.03Q69.46 44.86 69.46 46.17L69.46 46.17Q69.46 47.07 70.04 47.83Q70.63 48.59 72.46 49.21Q74.29 49.83 78.15 50.59L78.15 50.59Q82.91 51.35 85.74 53.04Q88.57 54.73 89.78 56.97Q90.99 59.21 90.99 61.70L90.99 61.70Q90.99 65.01 88.99 67.56Q86.98 70.11 83.15 71.53Q79.33 72.94 73.74 72.94Z" fill="#fff"></path></svg>
|
After Width: | Height: | Size: 2.5 KiB |
BIN
public/quickshell.png
Normal file
BIN
public/quickshell.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
18
src/components/Collapsible.tsx
Normal file
18
src/components/Collapsible.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { Collapsible } from "@ark-ui/solid";
|
||||
import type { Component, JSX } from "solid-js";
|
||||
import { CaretCircleRight } from "@icons";
|
||||
|
||||
export const DocsCollapsible: Component<{
|
||||
title: string;
|
||||
children: JSX.Element;
|
||||
}> = props => {
|
||||
return (
|
||||
<Collapsible.Root>
|
||||
<Collapsible.Trigger>
|
||||
<CaretCircleRight />
|
||||
{props.title}
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>{props.children}</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
);
|
||||
};
|
66
src/components/Header.astro
Normal file
66
src/components/Header.astro
Normal file
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
import { ThemeSelect } from "@components/hooks/ThemeSwitch";
|
||||
import { generateTypeData } from "@config/io/generateTypeData";
|
||||
import Nav from "@components/navigation/sidebars/Nav.astro";
|
||||
import TOC from "@components/navigation/sidebars/TOC.astro";
|
||||
import type { TypeTOC } from "./navigation/sidebars/types";
|
||||
import Search from "./navigation/Search.astro";
|
||||
|
||||
const routes = await generateTypeData();
|
||||
|
||||
const url = Astro.url.pathname.split("/");
|
||||
const currentClass = url[4];
|
||||
const currentData = routes.find(
|
||||
item => item.name === currentClass
|
||||
);
|
||||
|
||||
const data = currentData?.data;
|
||||
const tocFunctions =
|
||||
data?.functions?.map(item => item.name) || null;
|
||||
|
||||
const propsKeys = data?.properties
|
||||
? Object.keys(data.properties)
|
||||
: null;
|
||||
const signalKeys = data?.signals
|
||||
? Object.keys(data.signals)
|
||||
: null;
|
||||
const variantKeys = data?.variants
|
||||
? Object.keys(data.variants)
|
||||
: null;
|
||||
|
||||
let sidebarData: TypeTOC | undefined = {
|
||||
properties: propsKeys,
|
||||
functions: tocFunctions,
|
||||
signals: signalKeys,
|
||||
variants: variantKeys,
|
||||
};
|
||||
|
||||
if (!data) {
|
||||
sidebarData = undefined;
|
||||
}
|
||||
|
||||
const { headings } = Astro.props;
|
||||
---
|
||||
<div class="header">
|
||||
<div class="header-item header-left">
|
||||
<h3 class="header-title">
|
||||
<a href="/">Quickshell</a>
|
||||
</h3>
|
||||
{url.length > 2 ?
|
||||
<Nav mobile={true}/>
|
||||
: null}
|
||||
<div class="spacer-mobile">|</div>
|
||||
<h3 class="header-title mobile">
|
||||
<a href="/">Quickshell</a>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="header-item header-right">
|
||||
<Search/>
|
||||
<div class="spacer-desktop">|</div>
|
||||
<ThemeSelect client:load />
|
||||
<div class="spacer-mobile">|</div>
|
||||
{url.length > 2 ?
|
||||
<TOC headings={headings} types={sidebarData} mobile={true}/>
|
||||
: null}
|
||||
</div>
|
||||
</div>
|
30
src/components/MD_Title.tsx
Normal file
30
src/components/MD_Title.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import {
|
||||
type ParentComponent,
|
||||
onMount,
|
||||
createSignal,
|
||||
onCleanup,
|
||||
} from "solid-js";
|
||||
import { Hashtag } from "@icons";
|
||||
|
||||
const MD_Title: ParentComponent<{ titleVar: number }> = props => {
|
||||
const [_, setMounted] = createSignal<boolean>(false);
|
||||
|
||||
onMount(() => {
|
||||
setMounted(true);
|
||||
onCleanup(() => {
|
||||
setMounted(false);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div class={`heading heading-${props.titleVar}`}>
|
||||
<span class="heading-hashtag">
|
||||
<Hashtag />
|
||||
</span>
|
||||
{props.children}
|
||||
<hr />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default MD_Title;
|
52
src/components/hooks/CreateCopyButtons.astro
Normal file
52
src/components/hooks/CreateCopyButtons.astro
Normal file
|
@ -0,0 +1,52 @@
|
|||
---
|
||||
|
||||
---
|
||||
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
// code copy
|
||||
let blocks = document.querySelectorAll("pre");
|
||||
if (blocks.length > 0) {
|
||||
blocks.forEach((block) => {
|
||||
let button = document.createElement("button");
|
||||
button.className = "copy-button";
|
||||
button.innerHTML = `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M200 32h-36.26a47.92 47.92 0 0 0-71.48 0H56a16 16 0 0 0-16 16v168a16 16 0 0 0 16 16h144a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16m-72 0a32 32 0 0 1 32 32H96a32 32 0 0 1 32-32m72 184H56V48h26.75A47.9 47.9 0 0 0 80 64v8a8 8 0 0 0 8 8h80a8 8 0 0 0 8-8v-8a47.9 47.9 0 0 0-2.75-16H200Z"
|
||||
/>
|
||||
</svg>
|
||||
`;
|
||||
button.onclick = () => {
|
||||
let snippet = block.innerText ?? "";
|
||||
navigator.clipboard.writeText(snippet);
|
||||
button.classList.toggle("copied");
|
||||
setTimeout(() => button.classList.remove("copied"), 1000);
|
||||
};
|
||||
block.appendChild(button);
|
||||
});
|
||||
}
|
||||
}, 3001)
|
||||
|
||||
// heading copy
|
||||
let headings = document.getElementsByClassName("heading")
|
||||
if (headings.length > 0) {
|
||||
for (const heading of headings) {
|
||||
let button = heading.querySelector("span")
|
||||
if (button) {
|
||||
button.onclick = () => {
|
||||
let link = window.location.href.split("#")[0];
|
||||
link += `#-${heading.textContent?.slice(10).trimEnd().replaceAll(" ", "-").toLowerCase()}`;
|
||||
navigator.clipboard.writeText(link);
|
||||
heading.classList.toggle("copied")
|
||||
setTimeout(() => heading.classList.remove("copied"), 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
107
src/components/hooks/CreateQMLCodeButtons.astro
Normal file
107
src/components/hooks/CreateQMLCodeButtons.astro
Normal file
|
@ -0,0 +1,107 @@
|
|||
---
|
||||
|
||||
---
|
||||
<script>
|
||||
const qtRegExp = /QT_(\w+)/g
|
||||
const qsRegExp = /QS_(\w+)/g
|
||||
setTimeout(() =>{
|
||||
const blocks = document.querySelectorAll("pre")
|
||||
if (blocks.length > 0) {
|
||||
blocks.forEach((block) => {
|
||||
const content = block.textContent
|
||||
const elements = block.querySelectorAll("span")
|
||||
const classElements:HTMLSpanElement[] = [];
|
||||
if (elements.length === 0) {
|
||||
console.log("NO SPAN ELEMENTS FOUND")
|
||||
}
|
||||
|
||||
elements.forEach(element => {
|
||||
const isClassColored = element.style.cssText === "color: rgb(255, 203, 107);"
|
||||
const isSignal = element.innerText.trim().startsWith("on")
|
||||
const qualifier = isClassColored && !isSignal
|
||||
|
||||
if (qualifier) {
|
||||
const dotSibling = element.nextSibling
|
||||
const isSplit = dotSibling?.textContent === "."
|
||||
|
||||
if (isSplit) {
|
||||
let newInnerText = element.innerText + dotSibling.textContent + dotSibling.nextSibling?.textContent
|
||||
|
||||
if (dotSibling.nextSibling) {
|
||||
dotSibling.nextSibling.textContent !== " {"
|
||||
? dotSibling.nextSibling.remove()
|
||||
: null
|
||||
}
|
||||
|
||||
dotSibling.remove()
|
||||
element.innerText = newInnerText
|
||||
}
|
||||
classElements.push(element)
|
||||
}
|
||||
})
|
||||
|
||||
if (content) {
|
||||
const qtMatch = [...content.matchAll(qtRegExp)]
|
||||
const qsMatch = [...content.matchAll(qsRegExp)]
|
||||
|
||||
if (qtMatch.length > 0) {
|
||||
for (const qtMatching of qtMatch) {
|
||||
const newATag = document.createElement("a")
|
||||
const qtBelongs = qtMatching[0].split("_")[1].replace("11", "-") || null
|
||||
const qtClass = qtMatching[1].split("_")[1]
|
||||
const link = `https://doc.qt.io/qt-6/qml-${qtBelongs ? `${qtBelongs}-${qtClass.toLowerCase()}` : "qtquick-" + qtClass.toLowerCase()}.html`
|
||||
newATag.target = "_blank"
|
||||
newATag.href = link
|
||||
newATag.innerText = qtClass
|
||||
const homeElement = classElements.find(item => {
|
||||
const spacing = item.innerText.replace(qtMatching[0], "")
|
||||
if (item.innerText.trim() === qtMatching[0].trim()){
|
||||
newATag.innerText = spacing + qtClass
|
||||
return true
|
||||
}
|
||||
})
|
||||
if (homeElement) {
|
||||
homeElement.innerText = ""
|
||||
homeElement.appendChild(newATag)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (qsMatch.length > 0) {
|
||||
for (const qsMatching of qsMatch) {
|
||||
const newATag = document.createElement("a")
|
||||
|
||||
const qsBelongs = qsMatching[0].split("_")[1].replace("00", ".") || null
|
||||
const qsClass = qsMatching[1].split("_")[1]
|
||||
|
||||
const link = `/docs/types/${qsBelongs ? `${qsBelongs}/${qsClass}` : qsClass}`
|
||||
newATag.target = "_blank"
|
||||
newATag.href = link
|
||||
|
||||
const homeElement = classElements.find(item => {
|
||||
const existingItem = item.innerText.trim()
|
||||
const matchingItem = qsMatching[0].trim()
|
||||
const spacing = item.innerText.replace(existingItem, "")
|
||||
|
||||
if (existingItem === matchingItem) {
|
||||
newATag.innerText = spacing + qsClass
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
if (homeElement) {
|
||||
homeElement.innerText = ""
|
||||
|
||||
if (homeElement.nextSibling) {
|
||||
homeElement.nextSibling.textContent !== " {"
|
||||
? homeElement.nextSibling.textContent = ""
|
||||
: null
|
||||
}
|
||||
homeElement.appendChild(newATag)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},3000)
|
||||
</script>
|
29
src/components/hooks/TOCIntersectionObserver.astro
Normal file
29
src/components/hooks/TOCIntersectionObserver.astro
Normal file
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
---
|
||||
<script>
|
||||
window.addEventListener('DOMContentLoaded', () => {
|
||||
const observer = new IntersectionObserver(entries => {
|
||||
entries.forEach(entry => {
|
||||
const heading = entry.target.querySelector('h2, h3, h4, h5, h6')
|
||||
if(heading) {
|
||||
const id = heading.id
|
||||
if (entry.intersectionRatio > 0) {
|
||||
const desktopElement = document.querySelector(`.toc-wrapper li a[href="#${id}"]`);
|
||||
const mobileElement = document.querySelector(`.toc-wrapper-mobile li a[href="#${id}"]`);
|
||||
const element = mobileElement?.checkVisibility() ? mobileElement : desktopElement;
|
||||
element?.parentElement?.classList.add('active')
|
||||
} else {
|
||||
const desktopElement = document.querySelector(`.toc-wrapper li a[href="#${id}"]`);
|
||||
const mobileElement = document.querySelector(`.toc-wrapper-mobile li a[href="#${id}"]`);
|
||||
const element = mobileElement?.checkVisibility() ? mobileElement : desktopElement;
|
||||
element?.parentElement?.classList.remove('active')
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('section[data-heading-rank]').forEach((section) => {
|
||||
observer.observe(section);
|
||||
});
|
||||
});
|
||||
</script>
|
101
src/components/hooks/ThemeSwitch.tsx
Normal file
101
src/components/hooks/ThemeSwitch.tsx
Normal file
|
@ -0,0 +1,101 @@
|
|||
import {
|
||||
createSignal,
|
||||
createEffect,
|
||||
onCleanup,
|
||||
onMount,
|
||||
type VoidComponent,
|
||||
} from "solid-js";
|
||||
import { Sun, Moon } from "@icons";
|
||||
|
||||
interface ThemeProps {
|
||||
theme: "light" | "dark";
|
||||
system: "light" | "dark";
|
||||
}
|
||||
|
||||
const getCurrentTheme = (): ThemeProps => {
|
||||
if (
|
||||
typeof localStorage !== "undefined" &&
|
||||
(localStorage.theme === "dark" ||
|
||||
(!("theme" in localStorage) &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches))
|
||||
) {
|
||||
return {
|
||||
theme: "dark",
|
||||
system: window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light",
|
||||
};
|
||||
}
|
||||
return {
|
||||
theme: "light",
|
||||
system: window.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? "dark"
|
||||
: "light",
|
||||
};
|
||||
};
|
||||
|
||||
const updateTheme = () => {
|
||||
document.documentElement.classList.add("changing-theme");
|
||||
if (
|
||||
localStorage.theme === "dark" ||
|
||||
(!("theme" in localStorage) &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches)
|
||||
) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
document.documentElement.classList.remove("changing-theme");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const ThemeSelect: VoidComponent = () => {
|
||||
const [currentTheme, setCurrentTheme] = createSignal<ThemeProps>({
|
||||
theme: "dark",
|
||||
system: "dark",
|
||||
});
|
||||
const [mounted, setMounted] = createSignal(false);
|
||||
|
||||
const toggleTheme = () => {
|
||||
if (!mounted()) return;
|
||||
setCurrentTheme(getCurrentTheme());
|
||||
if (currentTheme()!.theme !== currentTheme()!.system) {
|
||||
localStorage.removeItem("theme");
|
||||
} else {
|
||||
localStorage.theme = currentTheme()!.theme === "dark" ? "light" : "dark";
|
||||
}
|
||||
updateTheme();
|
||||
setCurrentTheme(getCurrentTheme());
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
setMounted(true);
|
||||
setCurrentTheme(getCurrentTheme());
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
mediaQuery.addEventListener("change", updateTheme);
|
||||
window.addEventListener("storage", updateTheme);
|
||||
|
||||
onCleanup(() => {
|
||||
mediaQuery.removeEventListener("change", updateTheme);
|
||||
window.removeEventListener("storage", updateTheme);
|
||||
setMounted(false);
|
||||
});
|
||||
});
|
||||
|
||||
return (
|
||||
<div onclick={toggleTheme} class="theme-toggle">
|
||||
{(mounted() && currentTheme().theme === "light") ||
|
||||
currentTheme().system === "light" ? (
|
||||
<Sun class="theme-sun" />
|
||||
) : (
|
||||
<Moon class="theme-moon" />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
66
src/components/hooks/TransformLinks.astro
Normal file
66
src/components/hooks/TransformLinks.astro
Normal file
|
@ -0,0 +1,66 @@
|
|||
---
|
||||
---
|
||||
<script>
|
||||
import { getQMLTypeLinkObject, getQMLTypeLink, getIconForLink } from "@config/io/helpers"
|
||||
|
||||
const detailsData = document.getElementsByTagName("p")
|
||||
const innerItems = document.getElementsByClassName("typedata-details")
|
||||
if (detailsData) {
|
||||
for (const details of detailsData) {
|
||||
const linkRegex = /TYPE99(\w+.)99TYPE/g
|
||||
const mtypeExists = details.textContent?.match(linkRegex)
|
||||
|
||||
if (!mtypeExists || !details.textContent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const linkMatch = [...details.textContent.matchAll(linkRegex)]
|
||||
let textWithLinks = details.textContent;
|
||||
for (const matching of linkMatch) {
|
||||
if (details.textContent.indexOf(matching[0]) === -1){
|
||||
continue
|
||||
}
|
||||
const linkObject = getQMLTypeLinkObject(matching[1]);
|
||||
const link = getQMLTypeLink(linkObject);
|
||||
const icon = linkObject.mtype ? getIconForLink(linkObject.mtype, false) : null;
|
||||
|
||||
// for signal
|
||||
const bracketString = getIconForLink("func", false)
|
||||
|
||||
const newLink = `<span class="type${linkObject.mtype}-link typedata-link">${icon ? icon : ""}<a href=${link}>${linkObject.mname || linkObject.name}</a>${linkObject.mtype === "signal" ? bracketString : ""}</span>`;
|
||||
textWithLinks = textWithLinks.replace(matching[0], newLink)
|
||||
}
|
||||
details.innerHTML = textWithLinks
|
||||
}
|
||||
}
|
||||
if (innerItems){
|
||||
for (const innerItem of innerItems){
|
||||
const linkRegex = /TYPE99(\w+.)99TYPE/g
|
||||
const listItems = innerItem.getElementsByTagName("li")
|
||||
|
||||
for (const li of listItems){
|
||||
const mtypeExists = li.textContent?.match(linkRegex)
|
||||
if (!mtypeExists || !li.textContent){
|
||||
continue
|
||||
}
|
||||
const linkMatch = [...li.textContent.matchAll(linkRegex)]
|
||||
let textWithLinks = li.textContent;
|
||||
for (const matching of linkMatch) {
|
||||
if (li.textContent.indexOf(matching[0]) === -1){
|
||||
continue
|
||||
}
|
||||
const linkObject = getQMLTypeLinkObject(matching[1]);
|
||||
const link = getQMLTypeLink(linkObject);
|
||||
const icon = linkObject.mtype ? getIconForLink(linkObject.mtype, false) : null;
|
||||
|
||||
// for signal
|
||||
const bracketString = getIconForLink("func", false)
|
||||
|
||||
const newLink = `<span class="type${linkObject.mtype}-link typedata-link">${icon ? icon : ""}<a href=${link}>${linkObject.mname || linkObject.name}</a>${linkObject.mtype === "signal" ? bracketString : ""}</span>`;
|
||||
textWithLinks = textWithLinks.replace(matching[0], newLink)
|
||||
}
|
||||
li.innerHTML = textWithLinks
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
21
src/components/hooks/TransformMDCodeblocks.astro
Normal file
21
src/components/hooks/TransformMDCodeblocks.astro
Normal file
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
---
|
||||
<script>
|
||||
import { codeToHtml } from "shiki";
|
||||
const injectedMd = document.getElementById("injectedMd");
|
||||
if (injectedMd) {
|
||||
const preElements = document.getElementsByTagName("pre");
|
||||
for (const pre of preElements) {
|
||||
if (pre.textContent){
|
||||
const innerText = pre.innerText;
|
||||
pre.outerHTML =
|
||||
await codeToHtml(
|
||||
innerText,
|
||||
{
|
||||
lang: "qml", "theme":"material-theme-ocean"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
477
src/components/icons.tsx
Normal file
477
src/components/icons.tsx
Normal file
|
@ -0,0 +1,477 @@
|
|||
import type { VoidComponent } from "solid-js";
|
||||
|
||||
export const XToMenu: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Close Menu</title>
|
||||
<g
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M5 5L12 12L19 5">
|
||||
<animate
|
||||
fill="freeze"
|
||||
attributeName="d"
|
||||
dur="0.4s"
|
||||
values="M5 5L12 12L19 5;M5 5L12 5L19 5"
|
||||
/>
|
||||
</path>
|
||||
<path d="M12 12H12">
|
||||
<animate
|
||||
fill="freeze"
|
||||
attributeName="d"
|
||||
dur="0.4s"
|
||||
values="M12 12H12;M5 12H19"
|
||||
/>
|
||||
</path>
|
||||
<path d="M5 19L12 12L19 19">
|
||||
<animate
|
||||
fill="freeze"
|
||||
attributeName="d"
|
||||
dur="0.4s"
|
||||
values="M5 19L12 12L19 19;M5 19L12 19L19 19"
|
||||
/>
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const MenuToX: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Open Menu</title>
|
||||
<g
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-width="2"
|
||||
>
|
||||
<path d="M5 5L12 5L19 5">
|
||||
<animate
|
||||
fill="freeze"
|
||||
attributeName="d"
|
||||
dur="0.4s"
|
||||
values="M5 5L12 5L19 5;M5 5L12 12L19 5"
|
||||
/>
|
||||
</path>
|
||||
<path d="M5 12H19">
|
||||
<animate
|
||||
fill="freeze"
|
||||
attributeName="d"
|
||||
dur="0.4s"
|
||||
values="M5 12H19;M12 12H12"
|
||||
/>
|
||||
</path>
|
||||
<path d="M5 19L12 19L19 19">
|
||||
<animate
|
||||
fill="freeze"
|
||||
attributeName="d"
|
||||
dur="0.4s"
|
||||
values="M5 19L12 19L19 19;M5 19L12 12L19 19"
|
||||
/>
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Sun: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Light</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M120 40V16a8 8 0 0 1 16 0v24a8 8 0 0 1-16 0m72 88a64 64 0 1 1-64-64a64.07 64.07 0 0 1 64 64m-16 0a48 48 0 1 0-48 48a48.05 48.05 0 0 0 48-48M58.34 69.66a8 8 0 0 0 11.32-11.32l-16-16a8 8 0 0 0-11.32 11.32Zm0 116.68l-16 16a8 8 0 0 0 11.32 11.32l16-16a8 8 0 0 0-11.32-11.32M192 72a8 8 0 0 0 5.66-2.34l16-16a8 8 0 0 0-11.32-11.32l-16 16A8 8 0 0 0 192 72m5.66 114.34a8 8 0 0 0-11.32 11.32l16 16a8 8 0 0 0 11.32-11.32ZM48 128a8 8 0 0 0-8-8H16a8 8 0 0 0 0 16h24a8 8 0 0 0 8-8m80 80a8 8 0 0 0-8 8v24a8 8 0 0 0 16 0v-24a8 8 0 0 0-8-8m112-88h-24a8 8 0 0 0 0 16h24a8 8 0 0 0 0-16"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Moon: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Dark</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M233.54 142.23a8 8 0 0 0-8-2a88.08 88.08 0 0 1-109.8-109.8a8 8 0 0 0-10-10a104.84 104.84 0 0 0-52.91 37A104 104 0 0 0 136 224a103.1 103.1 0 0 0 62.52-20.88a104.84 104.84 0 0 0 37-52.91a8 8 0 0 0-1.98-7.98m-44.64 48.11A88 88 0 0 1 65.66 67.11a89 89 0 0 1 31.4-26A106 106 0 0 0 96 56a104.11 104.11 0 0 0 104 104a106 106 0 0 0 14.92-1.06a89 89 0 0 1-26.02 31.4"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const ShevronSmallDown: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Open</title>
|
||||
<g transform="rotate(-90 12 12)">
|
||||
<path
|
||||
stroke="currentColor"
|
||||
stroke-dasharray="8"
|
||||
stroke-dashoffset="8"
|
||||
stroke-linecap="round"
|
||||
stroke-width="2"
|
||||
d="M9 12L14 7M9 12L14 17"
|
||||
fill="currentColor"
|
||||
>
|
||||
<animate
|
||||
fill="freeze"
|
||||
attributeName="stroke-dashoffset"
|
||||
dur="0.3s"
|
||||
values="8;0"
|
||||
/>
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const CaretCircleRight: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Open</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M128 24a104 104 0 1 0 104 104A104.11 104.11 0 0 0 128 24m0 192a88 88 0 1 1 88-88a88.1 88.1 0 0 1-88 88m29.66-93.66a8 8 0 0 1 0 11.32l-40 40a8 8 0 0 1-11.32-11.32L140.69 128l-34.35-34.34a8 8 0 0 1 11.32-11.32Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Clipboard: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Copy code</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M200 32h-36.26a47.92 47.92 0 0 0-71.48 0H56a16 16 0 0 0-16 16v168a16 16 0 0 0 16 16h144a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16m-72 0a32 32 0 0 1 32 32H96a32 32 0 0 1 32-32m72 184H56V48h26.75A47.9 47.9 0 0 0 80 64v8a8 8 0 0 0 8 8h80a8 8 0 0 0 8-8v-8a47.9 47.9 0 0 0-2.75-16H200Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Hashtag: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Copy link</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M224 90h-51l8.89-48.93a6 6 0 1 0-11.8-2.14L160.81 90H109l8.89-48.93a6 6 0 0 0-11.8-2.14L96.81 90H48a6 6 0 0 0 0 12h46.63l-9.46 52H32a6 6 0 0 0 0 12h51l-8.9 48.93a6 6 0 0 0 4.83 7A5.6 5.6 0 0 0 80 222a6 6 0 0 0 5.89-4.93l9.3-51.07H147l-8.89 48.93a6 6 0 0 0 4.83 7a5.6 5.6 0 0 0 1.08.1a6 6 0 0 0 5.89-4.93l9.28-51.1H208a6 6 0 0 0 0-12h-46.63l9.46-52H224a6 6 0 0 0 0-12m-74.83 64h-51.8l9.46-52h51.8Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Tag: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M246.66 123.56L201 55.13A15.94 15.94 0 0 0 187.72 48H40a16 16 0 0 0-16 16v128a16 16 0 0 0 16 16h147.72a16 16 0 0 0 13.28-7.12l45.63-68.44a8 8 0 0 0 .03-8.88M187.72 192H40V64h147.72l42.66 64Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Subtitles: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M224 48H32a16 16 0 0 0-16 16v128a16 16 0 0 0 16 16h192a16 16 0 0 0 16-16V64a16 16 0 0 0-16-16m0 144H32V64h192zM48 136a8 8 0 0 1 8-8h16a8 8 0 0 1 0 16H56a8 8 0 0 1-8-8m160 0a8 8 0 0 1-8 8h-96a8 8 0 0 1 0-16h96a8 8 0 0 1 8 8m-48 32a8 8 0 0 1-8 8H56a8 8 0 0 1 0-16h96a8 8 0 0 1 8 8m48 0a8 8 0 0 1-8 8h-16a8 8 0 0 1 0-16h16a8 8 0 0 1 8 8"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Ruler: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m235.32 73.37l-52.69-52.68a16 16 0 0 0-22.63 0L20.68 160a16 16 0 0 0 0 22.63l52.69 52.68a16 16 0 0 0 22.63 0L235.32 96a16 16 0 0 0 0-22.63M84.68 224L32 171.31l32-32l26.34 26.35a8 8 0 0 0 11.32-11.32L75.31 128L96 107.31l26.34 26.35a8 8 0 0 0 11.32-11.32L107.31 96L128 75.31l26.34 26.35a8 8 0 0 0 11.32-11.32L139.31 64l32-32L224 84.69Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const RoundBrackets: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M40 128c0 58.29 34.67 80.25 36.15 81.16a8 8 0 0 1-8.27 13.7C66.09 221.78 24 195.75 24 128s42.09-93.78 43.88-94.86a8 8 0 0 1 8.26 13.7C74.54 47.83 40 69.82 40 128m148.12-94.86a8 8 0 0 0-8.27 13.7C181.33 47.75 216 69.71 216 128s-34.67 80.25-36.12 81.14a8 8 0 0 0 8.24 13.72C189.91 221.78 232 195.75 232 128s-42.09-93.78-43.88-94.86"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const PowerCord: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M149.66 138.34a8 8 0 0 0-11.32 0L120 156.69L99.31 136l18.35-18.34a8 8 0 0 0-11.32-11.32L88 124.69l-18.34-18.35a8 8 0 0 0-11.32 11.32l6.35 6.34l-23.32 23.31a32 32 0 0 0 0 45.26l5.38 5.37l-28.41 28.4a8 8 0 0 0 11.32 11.32l28.4-28.41l5.37 5.38a32 32 0 0 0 45.26 0L132 191.31l6.34 6.35a8 8 0 0 0 11.32-11.32L131.31 168l18.35-18.34a8 8 0 0 0 0-11.32m-52.29 65a16 16 0 0 1-22.62 0l-22.06-22.09a16 16 0 0 1 0-22.62L76 135.31L120.69 180Zm140.29-185a8 8 0 0 0-11.32 0l-28.4 28.41l-5.37-5.38a32.05 32.05 0 0 0-45.26 0L124 64.69l-6.34-6.35a8 8 0 0 0-11.32 11.32l80 80a8 8 0 0 0 11.32-11.32l-6.35-6.34l23.32-23.31a32 32 0 0 0 0-45.26l-5.38-5.37l28.41-28.4a8 8 0 0 0 0-11.32m-34.35 79L180 120.69L135.31 76l23.32-23.31a16 16 0 0 1 22.62 0l22.06 22a16 16 0 0 1 0 22.68Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const FourDiamonds: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M122.34 109.66a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32l-40-40a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 0 11.32ZM128 35.31L156.69 64L128 92.69L99.31 64Zm5.66 111a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 0 11.32l40 40a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32ZM128 220.69L99.31 192L128 163.31L156.69 192Zm109.66-98.35l-40-40a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 0 11.32l40 40a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32M192 156.69L163.31 128L192 99.31L220.69 128Zm-82.34-34.35l-40-40a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 0 11.32l40 40a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32M64 156.69L35.31 128L64 99.31L92.69 128Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Flag: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Flags</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M42.76 50A8 8 0 0 0 40 56v168a8 8 0 0 0 16 0v-44.23c26.79-21.16 49.87-9.75 76.45 3.41c16.4 8.11 34.06 16.85 53 16.85c13.93 0 28.54-4.75 43.82-18a8 8 0 0 0 2.76-6V56a8 8 0 0 0-13.27-6c-28 24.23-51.72 12.49-79.21-1.12C111.07 34.76 78.78 18.79 42.76 50M216 172.25c-26.79 21.16-49.87 9.74-76.45-3.41c-25-12.35-52.81-26.13-83.55-8.4V59.79c26.79-21.16 49.87-9.75 76.45 3.4c25 12.35 52.82 26.13 83.55 8.4Z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const ReturnKey: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Return</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M184 104v32a8 8 0 0 1-8 8H99.31l10.35 10.34a8 8 0 0 1-11.32 11.32l-24-24a8 8 0 0 1 0-11.32l24-24a8 8 0 0 1 11.32 11.32L99.31 128H168v-24a8 8 0 0 1 16 0m48-48v144a16 16 0 0 1-16 16H40a16 16 0 0 1-16-16V56a16 16 0 0 1 16-16h176a16 16 0 0 1 16 16m-16 144V56H40v144z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const ArrowRightElbow: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Return</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m221.66 181.66l-48 48a8 8 0 0 1-11.32-11.32L196.69 184H72a8 8 0 0 1-8-8V32a8 8 0 0 1 16 0v136h116.69l-34.35-34.34a8 8 0 0 1 11.32-11.32l48 48a8 8 0 0 1 0 11.32"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const ArrowLeftSimple: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Return</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M224 128a8 8 0 0 1-8 8H59.31l58.35 58.34a8 8 0 0 1-11.32 11.32l-72-72a8 8 0 0 1 0-11.32l72-72a8 8 0 0 1 11.32 11.32L59.31 120H216a8 8 0 0 1 8 8"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const Article: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Types</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M216 40H40a16 16 0 0 0-16 16v144a16 16 0 0 0 16 16h176a16 16 0 0 0 16-16V56a16 16 0 0 0-16-16m0 160H40V56h176zM184 96a8 8 0 0 1-8 8H80a8 8 0 0 1 0-16h96a8 8 0 0 1 8 8m0 32a8 8 0 0 1-8 8H80a8 8 0 0 1 0-16h96a8 8 0 0 1 8 8m0 32a8 8 0 0 1-8 8H80a8 8 0 0 1 0-16h96a8 8 0 0 1 8 8"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export const LoadingSpinner: VoidComponent<{
|
||||
class?: string;
|
||||
}> = props => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
class={props.class}
|
||||
>
|
||||
<title>Loading</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M2,12A11.2,11.2,0,0,1,13,1.05C12.67,1,12.34,1,12,1a11,11,0,0,0,0,22c.34,0,.67,0,1-.05C6,23,2,17.74,2,12Z"
|
||||
>
|
||||
<animateTransform
|
||||
attributeName="transform"
|
||||
dur="1.8s"
|
||||
repeatCount="indefinite"
|
||||
type="rotate"
|
||||
values="0 12 12;360 12 12"
|
||||
/>
|
||||
</path>
|
||||
</svg>
|
||||
);
|
||||
};
|
7
src/components/navigation/Search.astro
Normal file
7
src/components/navigation/Search.astro
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
import SearchComponent from "./search";
|
||||
---
|
||||
|
||||
<div class="search-wrapper">
|
||||
<SearchComponent client:only={"solid-js"}/>
|
||||
</div>
|
53
src/components/navigation/search/SearchModal.tsx
Normal file
53
src/components/navigation/search/SearchModal.tsx
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { For, type Component } from "solid-js";
|
||||
import type { SearchResult } from "./types";
|
||||
import {
|
||||
getIconForLink,
|
||||
getQMLTypeLink,
|
||||
getQMLTypeLinkObject,
|
||||
} from "@src/config/io/helpers";
|
||||
|
||||
const SearchModal: Component<{
|
||||
results: SearchResult[];
|
||||
}> = props => {
|
||||
const { results } = props;
|
||||
const linkRegex = /TYPE99(\w+.)99TYPE/g;
|
||||
|
||||
return (
|
||||
<div
|
||||
id="search-modal"
|
||||
class="search-output"
|
||||
>
|
||||
<For each={results}>
|
||||
{result => {
|
||||
let excerpt = result.excerpt;
|
||||
const linkMatch = [...excerpt.matchAll(linkRegex)];
|
||||
for (const match of linkMatch) {
|
||||
const unparsed = match[1];
|
||||
const linkObject = getQMLTypeLinkObject(unparsed);
|
||||
const linkParsed = getQMLTypeLink(linkObject);
|
||||
const icon = linkObject.mtype
|
||||
? getIconForLink(linkObject.mtype, false)
|
||||
: "";
|
||||
const bracketString = getIconForLink("func", false);
|
||||
const newString = `<span class="type${linkObject.mtype}-link typedata-link">${icon}<a href=${linkParsed}>${linkObject.mname || linkObject.name}</a>${linkObject.mtype === "signal" ? bracketString : ""}</span>`;
|
||||
excerpt = excerpt.replace(match[0], newString);
|
||||
}
|
||||
excerpt = `${excerpt}...`;
|
||||
return (
|
||||
<div class="search-output_item">
|
||||
<h3 class="search-output_heading">
|
||||
<a href={result.url}>{result.meta.title}</a>
|
||||
</h3>
|
||||
<section
|
||||
class="search-output_excerpt"
|
||||
innerHTML={excerpt}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchModal;
|
59
src/components/navigation/search/index.tsx
Normal file
59
src/components/navigation/search/index.tsx
Normal file
|
@ -0,0 +1,59 @@
|
|||
import {
|
||||
createResource,
|
||||
createSignal,
|
||||
type Component,
|
||||
} from "solid-js";
|
||||
|
||||
import type { SearchResult } from "./types";
|
||||
import SearchModal from "./SearchModal";
|
||||
|
||||
const pagefind = await import("@dist/pagefind/pagefind.js");
|
||||
pagefind.init();
|
||||
|
||||
async function PagefindSearch(query: string) {
|
||||
const search = await pagefind.search(query);
|
||||
const resultdata: SearchResult[] = [];
|
||||
for (const result of search.results) {
|
||||
const data = await result.data();
|
||||
resultdata.push(data);
|
||||
}
|
||||
return resultdata;
|
||||
}
|
||||
|
||||
const SearchComponent: Component = () => {
|
||||
let modal!: HTMLElement;
|
||||
const [query, setQuery] = createSignal("");
|
||||
const [results, { refetch }] = createResource(
|
||||
query,
|
||||
PagefindSearch
|
||||
);
|
||||
|
||||
function handleSearch(value: string) {
|
||||
setQuery(value);
|
||||
refetch();
|
||||
console.log(results());
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="search">
|
||||
<input
|
||||
id="search-input"
|
||||
type="text"
|
||||
role="searchbox"
|
||||
incremental
|
||||
value={query()}
|
||||
placeholder="Search"
|
||||
onChange={e => handleSearch(e.target.value)}
|
||||
//onfocusout={() => setQuery("")}
|
||||
/>{" "}
|
||||
{!results.loading && results() && results()!.length > 0 ? (
|
||||
<SearchModal
|
||||
results={results()!}
|
||||
ref={modal}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchComponent;
|
15
src/components/navigation/search/types.d.ts
vendored
Normal file
15
src/components/navigation/search/types.d.ts
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
interface SearchResult {
|
||||
url: string;
|
||||
excerpt: string;
|
||||
meta: {
|
||||
title: string;
|
||||
image?: string;
|
||||
};
|
||||
sub_results: {
|
||||
title: string;
|
||||
url: string;
|
||||
excerpt: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type { SearchResult }
|
31
src/components/navigation/sidebars/Nav.astro
Normal file
31
src/components/navigation/sidebars/Nav.astro
Normal file
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
import { generateTypeData } from "@config/io/generateTypeData";
|
||||
import { groupRoutes } from "@config/io/helpers";
|
||||
import NavComponent from "./nav";
|
||||
|
||||
const routes = await generateTypeData();
|
||||
const groupedRoutes = groupRoutes(routes);
|
||||
|
||||
const url = Astro.url.pathname.split("/");
|
||||
const currentRoute = url[2];
|
||||
const currentModule = url[3];
|
||||
const currentClass = url[4];
|
||||
|
||||
const treeProps = {
|
||||
items: groupedRoutes,
|
||||
currentRoute: currentRoute,
|
||||
currentModule: currentModule,
|
||||
currentClass: currentClass,
|
||||
};
|
||||
|
||||
const { mobile } = Astro.props;
|
||||
---
|
||||
|
||||
<aside class=`nav-wrapper${mobile ? "-mobile" : ""}`>
|
||||
<NavComponent
|
||||
routes={groupedRoutes}
|
||||
tree={treeProps}
|
||||
mobile={mobile}
|
||||
client:idle
|
||||
/>
|
||||
</aside>
|
21
src/components/navigation/sidebars/TOC.astro
Normal file
21
src/components/navigation/sidebars/TOC.astro
Normal file
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
import TableOfContents from "./toc";
|
||||
import type { ConfigHeading, TypeTOC } from "./types.d.ts";
|
||||
|
||||
export interface Props {
|
||||
headings?: ConfigHeading[];
|
||||
types?: TypeTOC;
|
||||
mobile: boolean;
|
||||
}
|
||||
|
||||
const { headings, types, mobile } = Astro.props;
|
||||
---
|
||||
|
||||
<div class=`toc-wrapper${mobile ? "-mobile":""}`>
|
||||
<TableOfContents
|
||||
config={headings}
|
||||
type={types}
|
||||
mobile={mobile}
|
||||
client:idle
|
||||
/>
|
||||
</div>
|
124
src/components/navigation/sidebars/nav/Tree.tsx
Normal file
124
src/components/navigation/sidebars/nav/Tree.tsx
Normal file
|
@ -0,0 +1,124 @@
|
|||
import { type Component, Index, For } from "solid-js";
|
||||
import { Accordion } from "@ark-ui/solid";
|
||||
|
||||
import { ShevronSmallDown } from "@icons";
|
||||
import type { TreeProps } from "../types";
|
||||
|
||||
export const Tree: Component<TreeProps> = props => {
|
||||
const { currentRoute, currentModule, currentClass, items } =
|
||||
props;
|
||||
|
||||
const typeKeys = items!.types && Object.keys(items!.types);
|
||||
|
||||
const tutorials =
|
||||
items!.tutorials && items!.tutorials
|
||||
? items!.tutorials.configuration
|
||||
: null;
|
||||
|
||||
return (
|
||||
<nav class="navtree">
|
||||
<Accordion.Root
|
||||
defaultValue={
|
||||
currentRoute === "types" ? ["Types"] : ["Configuration"]
|
||||
}
|
||||
collapsible
|
||||
multiple
|
||||
>
|
||||
<Accordion.Item value={"Configuration"}>
|
||||
<Accordion.ItemTrigger>
|
||||
<Accordion.ItemIndicator>
|
||||
<ShevronSmallDown class={"nav-shevron"} />
|
||||
</Accordion.ItemIndicator>
|
||||
<p>
|
||||
<a href={"/docs/configuration"}>Configuration</a>
|
||||
</p>
|
||||
</Accordion.ItemTrigger>
|
||||
<Accordion.ItemContent>
|
||||
<For each={tutorials}>
|
||||
{item => (
|
||||
<div
|
||||
class={`arktree-item ${currentModule === item.type ? "__current-type-doc" : ""}`}
|
||||
>
|
||||
<a href={`/docs/configuration/${item.type}`}>
|
||||
{item.name}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Accordion.ItemContent>
|
||||
</Accordion.Item>
|
||||
<Accordion.Item value={"Types"}>
|
||||
<Accordion.ItemTrigger>
|
||||
<Accordion.ItemIndicator>
|
||||
<ShevronSmallDown class={"nav-shevron"} />
|
||||
</Accordion.ItemIndicator>
|
||||
<p>
|
||||
<a href={"/docs/types"}>Type Definitions</a>
|
||||
</p>
|
||||
</Accordion.ItemTrigger>
|
||||
<Accordion.ItemContent>
|
||||
<Index each={typeKeys}>
|
||||
{typeKey => {
|
||||
return (
|
||||
<Accordion.Root
|
||||
defaultValue={
|
||||
currentModule === typeKey()
|
||||
? [typeKey()]
|
||||
: [""]
|
||||
}
|
||||
multiple
|
||||
collapsible
|
||||
>
|
||||
<Accordion.Item
|
||||
value={typeKey()}
|
||||
id={typeKey()}
|
||||
class={
|
||||
typeKey() === currentModule
|
||||
? "__current-type-doc"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<Accordion.ItemTrigger
|
||||
id={`${typeKey()}:button`}
|
||||
>
|
||||
<Accordion.ItemIndicator>
|
||||
<ShevronSmallDown
|
||||
class={"nav-shevron"}
|
||||
/>
|
||||
</Accordion.ItemIndicator>
|
||||
<p>
|
||||
<a href={`/docs/types/${typeKey()}`}>
|
||||
{typeKey()}
|
||||
</a>
|
||||
</p>
|
||||
</Accordion.ItemTrigger>
|
||||
<Accordion.ItemContent>
|
||||
<For each={items!.types[typeKey()]}>
|
||||
{submodule => (
|
||||
<div
|
||||
class={
|
||||
currentClass === submodule.name
|
||||
? "__current-type-doc"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
<a
|
||||
href={`/docs/types/${submodule.type}/${submodule.name}`}
|
||||
>
|
||||
{submodule.name}
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
</For>
|
||||
</Accordion.ItemContent>
|
||||
</Accordion.Item>
|
||||
</Accordion.Root>
|
||||
);
|
||||
}}
|
||||
</Index>
|
||||
</Accordion.ItemContent>
|
||||
</Accordion.Item>
|
||||
</Accordion.Root>
|
||||
</nav>
|
||||
);
|
||||
};
|
52
src/components/navigation/sidebars/nav/index.tsx
Normal file
52
src/components/navigation/sidebars/nav/index.tsx
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { createSignal, type Component } from "solid-js";
|
||||
|
||||
import { LoadingSpinner, MenuToX, XToMenu } from "@icons";
|
||||
import { Tree } from "./Tree";
|
||||
import type { NavProps } from "../types";
|
||||
|
||||
const NavComponent: Component<NavProps> = props => {
|
||||
const [open, setOpen] = createSignal<boolean>(false);
|
||||
const { tree, mobile, routes } = props;
|
||||
|
||||
if (!tree) {
|
||||
return <LoadingSpinner />;
|
||||
}
|
||||
|
||||
function toggle(e: MouseEvent) {
|
||||
e.preventDefault();
|
||||
setOpen(!open());
|
||||
}
|
||||
|
||||
if (!mobile) {
|
||||
return (
|
||||
<Tree
|
||||
currentRoute={tree.currentRoute}
|
||||
currentModule={tree.currentModule || null}
|
||||
currentClass={tree.currentClass || null}
|
||||
items={routes}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="nav-toggle">
|
||||
<div onclick={e => toggle(e)}>
|
||||
{open() ? (
|
||||
<MenuToX class="nav-icon" />
|
||||
) : (
|
||||
<XToMenu class="nav-icon" />
|
||||
)}
|
||||
</div>
|
||||
<div class={`nav-items ${open() ? "shown" : ""}`}>
|
||||
<Tree
|
||||
currentRoute={tree.currentRoute}
|
||||
currentModule={tree.currentModule}
|
||||
currentClass={tree.currentClass}
|
||||
items={routes}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default NavComponent;
|
33
src/components/navigation/sidebars/toc/Heading.tsx
Normal file
33
src/components/navigation/sidebars/toc/Heading.tsx
Normal file
|
@ -0,0 +1,33 @@
|
|||
import { For, type Component } from "solid-js";
|
||||
|
||||
import type { ConfigTOC } from "../types";
|
||||
|
||||
export const Heading: Component<{
|
||||
heading: ConfigTOC;
|
||||
index: number;
|
||||
}> = props => {
|
||||
const { heading, index } = props;
|
||||
|
||||
return (
|
||||
<li class={`toc_heading toc_heading-${index}`}>
|
||||
<a
|
||||
class="toc_a"
|
||||
href={`#${heading.slug}`}
|
||||
>
|
||||
{heading.text}
|
||||
</a>
|
||||
{heading.subheadings.length > 0 && (
|
||||
<ul>
|
||||
<For each={heading.subheadings}>
|
||||
{subheading => (
|
||||
<Heading
|
||||
heading={subheading}
|
||||
index={subheading.depth}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
};
|
111
src/components/navigation/sidebars/toc/Table.tsx
Normal file
111
src/components/navigation/sidebars/toc/Table.tsx
Normal file
|
@ -0,0 +1,111 @@
|
|||
import { type Component, For } from "solid-js";
|
||||
|
||||
import type { TypeTOC, ConfigTOC } from "../types";
|
||||
import {
|
||||
LoadingSpinner,
|
||||
Tag,
|
||||
RoundBrackets,
|
||||
PowerCord,
|
||||
FourDiamonds,
|
||||
} from "@icons";
|
||||
import { Heading } from "./Heading";
|
||||
|
||||
export const Table: Component<{
|
||||
typeTOC?: TypeTOC;
|
||||
configTOC?: ConfigTOC[];
|
||||
}> = props => {
|
||||
const { typeTOC, configTOC } = props;
|
||||
|
||||
if (configTOC) {
|
||||
return (
|
||||
<div class="toc-content">
|
||||
<p>Contents</p>
|
||||
<For each={configTOC}>
|
||||
{heading => (
|
||||
<Heading
|
||||
heading={heading}
|
||||
index={0}
|
||||
/>
|
||||
)}
|
||||
</For>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!typeTOC) {
|
||||
return <LoadingSpinner />;
|
||||
}
|
||||
|
||||
return (
|
||||
<nav class="toc-content">
|
||||
{typeTOC.properties ? (
|
||||
<ul class="types-list props-list">
|
||||
<For each={typeTOC.properties}>
|
||||
{prop => (
|
||||
<li class="types-item props-item">
|
||||
<Tag />
|
||||
<a
|
||||
class="type-anchor"
|
||||
href={`#${prop}`}
|
||||
>
|
||||
{prop}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
) : null}
|
||||
{typeTOC.functions ? (
|
||||
<ul class="types-list funcs-list">
|
||||
<For each={typeTOC.functions}>
|
||||
{func => (
|
||||
<li class="types-item func-item">
|
||||
<RoundBrackets />
|
||||
<a
|
||||
class="type-anchor"
|
||||
href={`#${func}`}
|
||||
>
|
||||
{func}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
) : null}
|
||||
{typeTOC.signals ? (
|
||||
<ul class="types-list signals-list">
|
||||
<For each={typeTOC.signals}>
|
||||
{signal => (
|
||||
<li class="types-item signals-item">
|
||||
<PowerCord />
|
||||
<a
|
||||
class="type-anchor"
|
||||
href={`#${signal}`}
|
||||
>
|
||||
{signal}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
) : null}
|
||||
{typeTOC.variants ? (
|
||||
<ul class="types-list vars-list">
|
||||
<For each={typeTOC.variants}>
|
||||
{variant => (
|
||||
<li class="types-item vars-item">
|
||||
<FourDiamonds />
|
||||
<a
|
||||
class="type-anchor"
|
||||
href={`#${variant}`}
|
||||
>
|
||||
{variant}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</For>
|
||||
</ul>
|
||||
) : null}
|
||||
</nav>
|
||||
);
|
||||
};
|
52
src/components/navigation/sidebars/toc/i.tsx.bak
Normal file
52
src/components/navigation/sidebars/toc/i.tsx.bak
Normal file
|
@ -0,0 +1,52 @@
|
|||
import { createSignal, type Component } from "solid-js";
|
||||
|
||||
import { Article } from "@icons";
|
||||
import { Table } from "./Table";
|
||||
import type {
|
||||
TOCProps,
|
||||
TypeTOC,
|
||||
ConfigHeading,
|
||||
} from "../types";
|
||||
import { buildHierarchy } from "@config/io/helpers";
|
||||
|
||||
const TableOfContents: Component<TOCProps> = props => {
|
||||
const [open, setOpen] = createSignal<boolean>(false);
|
||||
const [typeProps] = createSignal<TypeTOC | undefined>(
|
||||
props.type
|
||||
);
|
||||
const [configProps] = createSignal<
|
||||
ConfigHeading[] | undefined
|
||||
>(props.config);
|
||||
|
||||
function toggle(e: MouseEvent) {
|
||||
e.preventDefault();
|
||||
setOpen(!open());
|
||||
}
|
||||
|
||||
if (!props.mobile) {
|
||||
return typeProps() ? (
|
||||
<Table typeTOC={typeProps()} />
|
||||
) : (
|
||||
<Table configTOC={buildHierarchy(configProps()!)} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="menu-toggle">
|
||||
<div onclick={e => toggle(e)}>
|
||||
<Article />
|
||||
</div>
|
||||
<div class={`menu-items ${open() ? "shown" : ""}`}>
|
||||
{typeProps() ? (
|
||||
<Table typeTOC={typeProps()} />
|
||||
) : (
|
||||
<Table
|
||||
configTOC={buildHierarchy(configProps()!)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableOfContents;
|
41
src/components/navigation/sidebars/toc/index.tsx
Normal file
41
src/components/navigation/sidebars/toc/index.tsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { createSignal, type Component } from "solid-js";
|
||||
|
||||
import { Article } from "@icons";
|
||||
import { Table } from "./Table";
|
||||
import type { TOCProps } from "../types";
|
||||
import { buildHierarchy } from "@config/io/helpers";
|
||||
|
||||
const TableOfContents: Component<TOCProps> = props => {
|
||||
const [open, setOpen] = createSignal<boolean>(false);
|
||||
const { mobile, config, type } = props;
|
||||
|
||||
function toggle(e: MouseEvent) {
|
||||
e.preventDefault();
|
||||
setOpen(!open());
|
||||
}
|
||||
|
||||
if (!mobile) {
|
||||
return type ? (
|
||||
<Table typeTOC={type} />
|
||||
) : (
|
||||
<Table configTOC={buildHierarchy(config!)} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="toc-toggle">
|
||||
<div onclick={e => toggle(e)}>
|
||||
<Article />
|
||||
</div>
|
||||
<div class={`toc-mobile ${open() ? "shown" : ""}`}>
|
||||
{type ? (
|
||||
<Table typeTOC={type} />
|
||||
) : (
|
||||
<Table configTOC={buildHierarchy(config!)} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableOfContents;
|
63
src/components/navigation/sidebars/types.d.ts
vendored
Normal file
63
src/components/navigation/sidebars/types.d.ts
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Left
|
||||
export interface Item {
|
||||
name: string;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface GroupedRoutes {
|
||||
tutorials: { [key: string]: Item[] };
|
||||
types: { [key: string]: Item[] };
|
||||
}
|
||||
|
||||
export interface TreeProps {
|
||||
items: GroupedRoutes;
|
||||
currentRoute?: string;
|
||||
currentModule: string | null;
|
||||
currentClass: string | null;
|
||||
}
|
||||
|
||||
export interface NavProps {
|
||||
routes: GroupedRoutes;
|
||||
tree: TreeProps;
|
||||
mobile: boolean;
|
||||
}
|
||||
|
||||
// Right
|
||||
export interface TOCProps {
|
||||
config?: ConfigHeading[];
|
||||
type?: TypeTableProps;
|
||||
mobile: boolean;
|
||||
}
|
||||
|
||||
// -- Config
|
||||
export interface ConfigHeading {
|
||||
slug: string;
|
||||
text: string;
|
||||
depth: number;
|
||||
}
|
||||
|
||||
export interface ConfigTOC {
|
||||
slug: string;
|
||||
text: string;
|
||||
depth: number;
|
||||
subheadings: ConfigTOC[];
|
||||
}
|
||||
|
||||
export interface ConfigTableProps {
|
||||
content: {
|
||||
title: string;
|
||||
};
|
||||
headings: ConfigHeading[];
|
||||
frontmatter?: {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
}
|
||||
|
||||
// -- Types
|
||||
export interface TypeTOC {
|
||||
properties: string[] | null;
|
||||
functions: string[] | null;
|
||||
signals: string[] | null;
|
||||
variants: string[] | null;
|
||||
}
|
67
src/components/type/Functions.astro
Normal file
67
src/components/type/Functions.astro
Normal file
|
@ -0,0 +1,67 @@
|
|||
---
|
||||
import type {
|
||||
QMLTypeLinkObject,
|
||||
QuickshellFunction,
|
||||
} from "@config/io/types";
|
||||
import {
|
||||
parseMarkdown,
|
||||
getQMLTypeLink,
|
||||
} from "@config/io/helpers";
|
||||
import { Tag } from "@icons";
|
||||
|
||||
export interface Props {
|
||||
funcData: QuickshellFunction[];
|
||||
title: string;
|
||||
}
|
||||
|
||||
const { funcData, title } = Astro.props;
|
||||
---
|
||||
<ul class="typedata typefuncs">
|
||||
{
|
||||
funcData.map(item => {
|
||||
const functionParams = item.params.length > 0 ? item.params : null
|
||||
const retTypeLink = getQMLTypeLink(item.ret as unknown as QMLTypeLinkObject)
|
||||
return (
|
||||
<li id={item.name} class="typedata-root typefunc-root">
|
||||
<p class="typedata-name typefunc-name">
|
||||
{item.name}({functionParams
|
||||
? functionParams.map((itemType, index) => (
|
||||
<span class="typedata-param">{itemType.name}{
|
||||
index !== functionParams.length - 1
|
||||
&& ", "
|
||||
}</span>
|
||||
)
|
||||
) : null})<span class="type-datatype">: <a
|
||||
href={retTypeLink}
|
||||
target="_blank"
|
||||
>{item.ret.name || item.ret.type}</a></span>
|
||||
</p>
|
||||
{
|
||||
item.params.length > 0 ? (
|
||||
<p class="typedata-params typefunc-params">
|
||||
{
|
||||
item.params.map(param => {
|
||||
const paramTypeLink = getQMLTypeLink(param.type);
|
||||
return (
|
||||
<span class="typedata-param typefunc-param">
|
||||
<Tag client:idle/>
|
||||
{param.name}<span class="type-datatype">: <a
|
||||
href={paramTypeLink}
|
||||
target="_blank"
|
||||
>{param.type.name}</a></span>
|
||||
</span>
|
||||
)
|
||||
})
|
||||
}
|
||||
</p>
|
||||
)
|
||||
:null
|
||||
}
|
||||
<section class="typedata-details">
|
||||
<div class="typedata-detailsdata" set:html={parseMarkdown(item.details, title)}/>
|
||||
</section>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
83
src/components/type/Properties.astro
Normal file
83
src/components/type/Properties.astro
Normal file
|
@ -0,0 +1,83 @@
|
|||
---
|
||||
import {
|
||||
parseMarkdown,
|
||||
getQMLTypeLink,
|
||||
} from "@config/io/helpers";
|
||||
import type {
|
||||
QMLTypeLinkObject,
|
||||
QuickshellProps,
|
||||
} from "@config/io/types";
|
||||
import { Tag, Flag } from "@icons";
|
||||
|
||||
export interface Props {
|
||||
propsKeys: string[];
|
||||
propsData: QuickshellProps;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const { propsKeys, propsData, title } = Astro.props;
|
||||
---
|
||||
<ul class="typedata typeprops">
|
||||
{
|
||||
propsKeys.map(item => {
|
||||
const propData = propsData[item]
|
||||
let typeLink:string;
|
||||
let linkText:string;
|
||||
const gadget = propData.type.gadget;
|
||||
if (gadget) {
|
||||
typeLink = "#"
|
||||
linkText = `[${Object.keys(gadget).toString()}]`
|
||||
} else {
|
||||
typeLink = getQMLTypeLink(propData.type as unknown as QMLTypeLinkObject)
|
||||
linkText = propData.type.name || propData.type.type
|
||||
}
|
||||
return (
|
||||
<li id={ item } class="typedata-root typeprop-root">
|
||||
<p class="typedata-name typeprop-name">
|
||||
<Tag client:idle/>
|
||||
{ item }<span class="type-datatype">: <a
|
||||
href={typeLink}
|
||||
target="_blank"
|
||||
>{ linkText }</a></span>
|
||||
</p>
|
||||
{
|
||||
propData.flags && propData.flags.length > 0 ? (
|
||||
<p class="type-flags">
|
||||
{
|
||||
propData.flags.map((flag) => {
|
||||
return (
|
||||
<span class="type-flag">
|
||||
<Flag client:idle/>
|
||||
{flag}
|
||||
</span>
|
||||
)
|
||||
})
|
||||
}
|
||||
</p>
|
||||
) : null
|
||||
}
|
||||
{
|
||||
gadget ? (
|
||||
<p class="typedata-params typefunc-params">
|
||||
{
|
||||
Object.keys(gadget).map((key) => {
|
||||
const gadgetData = gadget[key]
|
||||
return (
|
||||
<span class="typedata-param typefunc-param">
|
||||
<Tag client:idle/>
|
||||
{key}:<span><a href=`${getQMLTypeLink(gadgetData as unknown as QMLTypeLinkObject)}`>{gadgetData.name}</a></span>
|
||||
</span>
|
||||
)
|
||||
})
|
||||
}
|
||||
</p>
|
||||
):null
|
||||
}
|
||||
<section class="typedata-details">
|
||||
<div id="injectedMd" class="typedata-detailsdata" set:html={parseMarkdown(propData.details, title)} />
|
||||
</section>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
54
src/components/type/Signals.astro
Normal file
54
src/components/type/Signals.astro
Normal file
|
@ -0,0 +1,54 @@
|
|||
---
|
||||
import type { QuickshellSignal } from "@config/io/types";
|
||||
import { Tag, PowerCord } from "@icons";
|
||||
import { parseMarkdown } from "@config/io/helpers";
|
||||
|
||||
export interface Props {
|
||||
signalKeys: string[];
|
||||
signalsData: QuickshellSignal;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const { signalKeys, signalsData, title } = Astro.props;
|
||||
---
|
||||
<ul class="typedata typesignals">
|
||||
{
|
||||
signalKeys.map(item => {
|
||||
const signalData = signalsData[item];
|
||||
const paramKeys = signalData.params.length > 0 ? signalData.params.map((param,index) => `${param.name}${index !== signalData.params.length -1 ? ", ":""}`) : []
|
||||
return (
|
||||
<li id={ item } class="typedata-root typesignal-root">
|
||||
<p class="typedata-name typesignal-name">
|
||||
<PowerCord client:idle/>
|
||||
{ item }(<span class="typedata-param">{paramKeys}</span>)<span class="typesignal-doclink"><a
|
||||
href="/docs/configuration/qml-overview#-signals"
|
||||
target="_blank"
|
||||
>?</a></span>
|
||||
</p>
|
||||
{
|
||||
signalData.params && signalData.params.length > 0 ? (
|
||||
<p class="typesignal-params">
|
||||
{
|
||||
signalData.params.map((param, _) => {
|
||||
return (
|
||||
<span class="typesignal-param typedata-param">
|
||||
<Tag client:idle/>
|
||||
{param.name}<span class="type-datatype">: <a
|
||||
href=""
|
||||
target="_blank"
|
||||
>{param.type.name}</a></span>
|
||||
</span>
|
||||
)
|
||||
})
|
||||
}
|
||||
</p>
|
||||
) : null
|
||||
}
|
||||
<section class="typedata-details">
|
||||
<div class="typedata-detailsdata" set:html={parseMarkdown(signalData.details, title)} />
|
||||
</section>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
45
src/components/type/Variants.astro
Normal file
45
src/components/type/Variants.astro
Normal file
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
import type { QuickshellVariant } from "@config/io/types";
|
||||
import { FourDiamonds } from "../icons";
|
||||
import { parseMarkdown } from "@src/config/io/helpers";
|
||||
|
||||
export interface Props {
|
||||
variantKeys: string[];
|
||||
variantsData: QuickshellVariant;
|
||||
title: string;
|
||||
}
|
||||
|
||||
const { variantKeys, variantsData, title } = Astro.props;
|
||||
---
|
||||
<ul class="typedata typevariants">
|
||||
{
|
||||
variantKeys.map(item => {
|
||||
const variantData = variantsData[item];
|
||||
const paramKeys = variantData.params && variantData.params.length > 0
|
||||
? variantData.params.map(param => param.name)
|
||||
: [];
|
||||
return (
|
||||
<li id={ item } class="typedata-root typevariant-root">
|
||||
<p class="typedata-name typevariant-name">
|
||||
<FourDiamonds client:idle/>
|
||||
{ item }
|
||||
</p>
|
||||
{
|
||||
paramKeys ? (
|
||||
<div class="typedata-params typevariant-params">
|
||||
{paramKeys.map(paramKey => (
|
||||
<span class="typedata-param typevariant-param">{paramKey}</span>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
:null
|
||||
}
|
||||
<section class="typedata-details">
|
||||
<div class="typedata-detailsdata" set:html={parseMarkdown(variantData.details, title)} />
|
||||
</section>
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
|
28
src/config/Head.astro
Normal file
28
src/config/Head.astro
Normal file
|
@ -0,0 +1,28 @@
|
|||
---
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
// image: string;
|
||||
}
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<title>{title}</title>
|
||||
<meta name="description" content={description} />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
|
||||
<!-- Open Graph Meta Tags -->
|
||||
<meta name="og:type" content="website" />
|
||||
<meta name="og:site_name" content="quickshell" />
|
||||
<meta name="og:url" content={Astro.url} />
|
||||
<meta name="og:title" content={title} />
|
||||
<meta name="og:description" content={description} />
|
||||
<!-- <meta name="og:image" content={image} /> -->
|
||||
|
||||
<!-- Twitter Meta Tags -->
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="twitter:domain" content="quickshell.outfoxxed.me" />
|
||||
<meta name="twitter:url" content={Astro.url} />
|
||||
<meta name="twitter:title" content={title} />
|
||||
<meta name="twitter:description" content={description} />
|
||||
<!-- <meta name="twitter:image" content={image} /> -->
|
23
src/config/PreTheme.astro
Normal file
23
src/config/PreTheme.astro
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
|
||||
---
|
||||
|
||||
<script is:inline>
|
||||
function updateTheme() {
|
||||
if (
|
||||
localStorage.theme === "dark" ||
|
||||
(!("theme" in localStorage) &&
|
||||
window.matchMedia("(prefers-color-scheme: dark)").matches)
|
||||
) {
|
||||
document.documentElement.classList.add("dark");
|
||||
} else {
|
||||
document.documentElement.classList.remove("dark");
|
||||
}
|
||||
}
|
||||
|
||||
// Run on initial load
|
||||
updateTheme();
|
||||
|
||||
// Run on view transitions
|
||||
document.addEventListener("astro:after-swap", updateTheme);
|
||||
</script>
|
5
src/config/io/fuseConfig.ts
Normal file
5
src/config/io/fuseConfig.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export const options = {
|
||||
includeMatches: true,
|
||||
minMatchCharLength: 2,
|
||||
threshold: 0.5,
|
||||
};
|
0
src/config/io/generateSearchLists.ts
Normal file
0
src/config/io/generateSearchLists.ts
Normal file
53
src/config/io/generateTypeData.ts
Normal file
53
src/config/io/generateTypeData.ts
Normal file
|
@ -0,0 +1,53 @@
|
|||
import { promises as fs } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import type { RouteData, dirData } from "./types";
|
||||
|
||||
async function readSubdir(subdir: string): Promise<dirData[]> {
|
||||
const fullpath = path.join(process.cwd(), "modules", subdir);
|
||||
const filenames = await fs.readdir(fullpath);
|
||||
|
||||
const data = await Promise.all(
|
||||
filenames.map(async filename => {
|
||||
const filepath = path.join(fullpath, filename);
|
||||
const content = await fs.readFile(filepath, "utf8");
|
||||
const data = JSON.parse(content);
|
||||
if (typeof data.module === "undefined") {
|
||||
data.module = "index";
|
||||
data.contains = filenames
|
||||
.filter(filename => filename !== "index.json")
|
||||
.map(filename => filename.replace(".json", ""));
|
||||
}
|
||||
const returnValue = {
|
||||
fullpath: path.join(fullpath, filename),
|
||||
filename: filename.replace(".json", ""),
|
||||
category: subdir,
|
||||
data: data,
|
||||
};
|
||||
return returnValue;
|
||||
})
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
export async function generateTypeData(): Promise<RouteData[]> {
|
||||
const mainDir = path.join(process.cwd(), "modules");
|
||||
const subdirs = await fs.readdir(mainDir, {
|
||||
withFileTypes: true,
|
||||
});
|
||||
const routes: RouteData[] = [];
|
||||
|
||||
for (const subdir of subdirs) {
|
||||
const data = await readSubdir(subdir.name);
|
||||
const returnValue = data.map(entry => {
|
||||
return {
|
||||
type: entry.category,
|
||||
name: entry.filename,
|
||||
path: entry.fullpath,
|
||||
data: entry.data,
|
||||
};
|
||||
});
|
||||
routes.push(...returnValue);
|
||||
}
|
||||
return routes;
|
||||
}
|
238
src/config/io/helpers.ts
Normal file
238
src/config/io/helpers.ts
Normal file
|
@ -0,0 +1,238 @@
|
|||
import { marked } from "marked";
|
||||
import markedAlert from "marked-alert";
|
||||
|
||||
import {
|
||||
// Flag,
|
||||
PowerCord,
|
||||
Tag,
|
||||
FourDiamonds,
|
||||
RoundBrackets,
|
||||
} from "@icons";
|
||||
import type {
|
||||
ConfigHeading,
|
||||
ConfigTOC,
|
||||
GroupedRoutes,
|
||||
} from "@components/navigation/sidebars/types";
|
||||
import type { QMLTypeLinkObject, RouteData } from "./types";
|
||||
|
||||
export function buildHierarchy(headings: ConfigHeading[]) {
|
||||
const toc: ConfigTOC[] = [];
|
||||
const parentHeadings = new Map();
|
||||
|
||||
if (!headings || headings.length === 0) {
|
||||
return toc;
|
||||
}
|
||||
|
||||
for (const h of headings) {
|
||||
const heading = { ...h, subheadings: [] };
|
||||
parentHeadings.set(heading.depth, heading);
|
||||
|
||||
if (heading.depth === 1) {
|
||||
toc.push(heading);
|
||||
} else {
|
||||
parentHeadings
|
||||
.get(heading.depth - 1)
|
||||
.subheadings.push(heading);
|
||||
}
|
||||
}
|
||||
return toc;
|
||||
}
|
||||
|
||||
export function groupRoutes(routes: RouteData[]): GroupedRoutes {
|
||||
const froutes = routes.filter(route => route.name !== "index");
|
||||
const defaultValue = {
|
||||
tutorials: {
|
||||
configuration: [
|
||||
{ name: "Intro", type: "intro" },
|
||||
{ name: "Positioning", type: "positioning" },
|
||||
{ name: "QML Overview", type: "qml-overview" },
|
||||
],
|
||||
},
|
||||
types: {},
|
||||
};
|
||||
return froutes.reduce<GroupedRoutes>((acc, route) => {
|
||||
if (!acc.tutorials) {
|
||||
acc.tutorials = {
|
||||
configuration: [
|
||||
{ name: "Intro", type: "intro" },
|
||||
{ name: "Positioning", type: "positioning" },
|
||||
{ name: "QML Overview", type: "qml-overview" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (!acc.types) acc.types = {};
|
||||
|
||||
if (!acc.types[route.type]) {
|
||||
acc.types[route.type] = [];
|
||||
}
|
||||
|
||||
acc.types[route.type].push({
|
||||
name: route.name,
|
||||
type: route.type,
|
||||
});
|
||||
return acc;
|
||||
}, defaultValue);
|
||||
}
|
||||
|
||||
export function parseMarkdown(text?: string, title?: string) {
|
||||
if (!text) {
|
||||
return marked.parse(`${title}`);
|
||||
}
|
||||
|
||||
return marked.use(markedAlert()).parse(text);
|
||||
}
|
||||
|
||||
export function getQMLTypeLinkObject(unparsed: string) {
|
||||
const isLocal = unparsed.startsWith("MQS_") ? "local" : false;
|
||||
const isQT = unparsed.startsWith("MQT_") ? "qt" : false;
|
||||
const index = isLocal || isQT || "self";
|
||||
|
||||
const hashMap = {
|
||||
local: () => {
|
||||
const linkSplit = unparsed.slice(4).split("99");
|
||||
const hasSubmodule = linkSplit[0].indexOf("_") !== -1;
|
||||
const linkModule = hasSubmodule
|
||||
? linkSplit[0].replace("_", ".")
|
||||
: linkSplit[0];
|
||||
const linkObj: QMLTypeLinkObject = {
|
||||
type: "local",
|
||||
module: linkModule.replace("_", "."),
|
||||
name: linkSplit[1].slice(1),
|
||||
};
|
||||
if (linkSplit.length > 2) {
|
||||
linkObj.mname = linkSplit[2].slice(1);
|
||||
linkObj.mtype = linkSplit[3].slice(1);
|
||||
}
|
||||
return linkObj;
|
||||
},
|
||||
qt: () => {
|
||||
const linkSplit = unparsed.slice(4).split("99");
|
||||
const hasSubmodule = linkSplit[0].indexOf("_") !== -1;
|
||||
const linkModule = hasSubmodule
|
||||
? linkSplit[0].replace("_", "-")
|
||||
: linkSplit[0];
|
||||
const linkObj: QMLTypeLinkObject = {
|
||||
type: "qt",
|
||||
module: linkModule,
|
||||
name: linkSplit[1].slice(1),
|
||||
};
|
||||
if (linkSplit.length > 2) {
|
||||
linkObj.mname = linkSplit[3];
|
||||
linkObj.mtype = linkSplit[4];
|
||||
}
|
||||
return linkObj;
|
||||
},
|
||||
self: () => {
|
||||
const linkSplit = unparsed.slice(1).split("99");
|
||||
const linkObj: QMLTypeLinkObject = {
|
||||
type: "self",
|
||||
mname: linkSplit[0],
|
||||
mtype: linkSplit[1].slice(1),
|
||||
};
|
||||
return linkObj;
|
||||
},
|
||||
};
|
||||
|
||||
return hashMap[index]();
|
||||
}
|
||||
|
||||
export function getQMLTypeLink({
|
||||
type,
|
||||
module,
|
||||
name,
|
||||
//mtype,
|
||||
mname,
|
||||
}: QMLTypeLinkObject) {
|
||||
if (type === "unknown") {
|
||||
return "#unknown";
|
||||
}
|
||||
const qtStart = "https://doc.qt.io/qt-6/";
|
||||
const localStart = "/docs/types";
|
||||
const isSpecific = mname ? `#${mname}` : "";
|
||||
|
||||
const hashMap = {
|
||||
local: () => {
|
||||
const localLink = `${localStart}/${module}/${name}${isSpecific}`;
|
||||
return localLink;
|
||||
},
|
||||
qt: () => {
|
||||
const qtLink = `${qtStart}${module!.toLowerCase().replace(".", "-")}-${name!.toLowerCase()}${isSpecific.toLowerCase()}.html`;
|
||||
return qtLink;
|
||||
},
|
||||
self: () => {
|
||||
const selfLink = `#${mname}`;
|
||||
return selfLink;
|
||||
},
|
||||
};
|
||||
|
||||
if (!type) {
|
||||
type = "self";
|
||||
}
|
||||
|
||||
return hashMap[type as keyof typeof hashMap]();
|
||||
}
|
||||
|
||||
export function getIconForLink(mtype: string, isJsx: boolean) {
|
||||
const TagIconString: string = `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class=""
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M246.66 123.56L201 55.13A15.94 15.94 0 0 0 187.72 48H40a16 16 0 0 0-16 16v128a16 16 0 0 0 16 16h147.72a16 16 0 0 0 13.28-7.12l45.63-68.44a8 8 0 0 0 .03-8.88M187.72 192H40V64h147.72l42.66 64Z"
|
||||
/>
|
||||
</svg>`;
|
||||
const RoundBracketsIconString: string = `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class=""
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M40 128c0 58.29 34.67 80.25 36.15 81.16a8 8 0 0 1-8.27 13.7C66.09 221.78 24 195.75 24 128s42.09-93.78 43.88-94.86a8 8 0 0 1 8.26 13.7C74.54 47.83 40 69.82 40 128m148.12-94.86a8 8 0 0 0-8.27 13.7C181.33 47.75 216 69.71 216 128s-34.67 80.25-36.12 81.14a8 8 0 0 0 8.24 13.72C189.91 221.78 232 195.75 232 128s-42.09-93.78-43.88-94.86"
|
||||
/>
|
||||
</svg>`;
|
||||
const PowerCordIconString: string = `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class=""
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M149.66 138.34a8 8 0 0 0-11.32 0L120 156.69L99.31 136l18.35-18.34a8 8 0 0 0-11.32-11.32L88 124.69l-18.34-18.35a8 8 0 0 0-11.32 11.32l6.35 6.34l-23.32 23.31a32 32 0 0 0 0 45.26l5.38 5.37l-28.41 28.4a8 8 0 0 0 11.32 11.32l28.4-28.41l5.37 5.38a32 32 0 0 0 45.26 0L132 191.31l6.34 6.35a8 8 0 0 0 11.32-11.32L131.31 168l18.35-18.34a8 8 0 0 0 0-11.32m-52.29 65a16 16 0 0 1-22.62 0l-22.06-22.09a16 16 0 0 1 0-22.62L76 135.31L120.69 180Zm140.29-185a8 8 0 0 0-11.32 0l-28.4 28.41l-5.37-5.38a32.05 32.05 0 0 0-45.26 0L124 64.69l-6.34-6.35a8 8 0 0 0-11.32 11.32l80 80a8 8 0 0 0 11.32-11.32l-6.35-6.34l23.32-23.31a32 32 0 0 0 0-45.26l-5.38-5.37l28.41-28.4a8 8 0 0 0 0-11.32m-34.35 79L180 120.69L135.31 76l23.32-23.31a16 16 0 0 1 22.62 0l22.06 22a16 16 0 0 1 0 22.68Z"
|
||||
/>
|
||||
</svg>`;
|
||||
const FourDiamondsIconString: string = `<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
class=""
|
||||
>
|
||||
<title>Go to</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M122.34 109.66a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32l-40-40a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 0 11.32ZM128 35.31L156.69 64L128 92.69L99.31 64Zm5.66 111a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 0 11.32l40 40a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32ZM128 220.69L99.31 192L128 163.31L156.69 192Zm109.66-98.35l-40-40a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 0 11.32l40 40a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32M192 156.69L163.31 128L192 99.31L220.69 128Zm-82.34-34.35l-40-40a8 8 0 0 0-11.32 0l-40 40a8 8 0 0 0 0 11.32l40 40a8 8 0 0 0 11.32 0l40-40a8 8 0 0 0 0-11.32M64 156.69L35.31 128L64 99.31L92.69 128Z"
|
||||
/>
|
||||
</svg>`;
|
||||
const map = {
|
||||
prop: () => (isJsx ? Tag : TagIconString),
|
||||
func: () => (isJsx ? RoundBrackets : RoundBracketsIconString),
|
||||
signal: () => (isJsx ? PowerCord : PowerCordIconString),
|
||||
variant: () =>
|
||||
isJsx ? FourDiamonds : FourDiamondsIconString,
|
||||
};
|
||||
|
||||
return map[mtype as keyof typeof map]();
|
||||
}
|
121
src/config/io/types.d.ts
vendored
Normal file
121
src/config/io/types.d.ts
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
//#FIXME fuseConfig.ts
|
||||
// --
|
||||
|
||||
// generateSearchLists.ts
|
||||
interface SearchLists {
|
||||
slug: string;
|
||||
link: string;
|
||||
summary: string;
|
||||
}
|
||||
// --
|
||||
|
||||
// generateTypeData.ts
|
||||
interface QuickshellBase {
|
||||
type: string;
|
||||
module: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface QuickshellInstance {
|
||||
name?: string;
|
||||
type: {
|
||||
gadget?: QuickshellGadget;
|
||||
type: string;
|
||||
module: string;
|
||||
name: string;
|
||||
of?: QuickshellBase;
|
||||
};
|
||||
details?: string;
|
||||
flags?: string[];
|
||||
}
|
||||
|
||||
interface QuickshellGadget {
|
||||
[key: string]: QuickshellInstance;
|
||||
}
|
||||
|
||||
interface QuickshellProps {
|
||||
[key: string]: QuickshellInstance;
|
||||
}
|
||||
|
||||
interface QuickshellFunction {
|
||||
ret: QuickshellInstance;
|
||||
name: string;
|
||||
id: string;
|
||||
details: string;
|
||||
params: QuickshellInstance[];
|
||||
}
|
||||
|
||||
interface QuickshellSignal {
|
||||
[key: string]: {
|
||||
name: string;
|
||||
details: string;
|
||||
params: QuickshellInstance[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface QuickshellVariant {
|
||||
[key: string]: {
|
||||
name?: string;
|
||||
details: string;
|
||||
params?: QuickshellInstance[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface QuickshellData {
|
||||
type: string;
|
||||
module: string;
|
||||
name: string;
|
||||
description: string;
|
||||
details: string;
|
||||
flags?: string[];
|
||||
contains?: string[];
|
||||
super?: QuickshellBase;
|
||||
properties?: QuickshellProps;
|
||||
functions?: QuickshellFunction[];
|
||||
signals?: QuickshellSignal;
|
||||
variants?: QuickshellVariant;
|
||||
subtypes?: QuickshellData[];
|
||||
}
|
||||
|
||||
export interface RouteData {
|
||||
// priority 1: Quickshell, Quickshell.Io, etc.
|
||||
type: string;
|
||||
// priority 1.1: entry name (e.g. DataStreamParser)
|
||||
name: string;
|
||||
// path to json
|
||||
path: string;
|
||||
// data content of the route
|
||||
data: QuickshellData;
|
||||
}
|
||||
|
||||
export interface dirData {
|
||||
fullpath: string;
|
||||
filename: string;
|
||||
category: string;
|
||||
data: QuickshellData;
|
||||
}
|
||||
// --
|
||||
|
||||
// helpers.ts
|
||||
interface QMLTypeLinkObject {
|
||||
type: string;
|
||||
module?: string;
|
||||
name?: string;
|
||||
mtype?: string;
|
||||
mname?: string;
|
||||
}
|
||||
// --
|
||||
|
||||
export type {
|
||||
QuickshellBase,
|
||||
QuickshellInstance,
|
||||
QuickshellGadget,
|
||||
QuickshellProps,
|
||||
QuickshellFunction,
|
||||
QuickshellSignal,
|
||||
QuickshellVariant,
|
||||
QuickshellData,
|
||||
RouteData,
|
||||
dirData,
|
||||
QMLTypeLinkObject,
|
||||
};
|
2
src/env.d.ts
vendored
Normal file
2
src/env.d.ts
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
/// <reference path="../.astro/types.d.ts" />
|
||||
/// <reference types="astro/client" />
|
29
src/layouts/BaseLayout.astro
Normal file
29
src/layouts/BaseLayout.astro
Normal file
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
import Header from "@components/Header.astro";
|
||||
import Head from "@config/Head.astro";
|
||||
import PreTheme from "@config/PreTheme.astro";
|
||||
import "@styles/global.css";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
image: string;
|
||||
}
|
||||
const { title, description } = Astro.props;
|
||||
---
|
||||
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<Head description={description} title={title} />
|
||||
<link rel="canonical" href={Astro.url} />
|
||||
<PreTheme />
|
||||
</head>
|
||||
<body class="baselayout">
|
||||
<Header />
|
||||
<h1>{title}</h1>
|
||||
<slot />
|
||||
</body>
|
||||
</html>
|
39
src/layouts/ConfigLayout.astro
Normal file
39
src/layouts/ConfigLayout.astro
Normal file
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
import DocsLayout from "@layouts/DocsLayout.astro";
|
||||
import TOCIntersectionObserver from "@src/components/hooks/TOCIntersectionObserver.astro";
|
||||
import TOC from "@components/navigation/sidebars/TOC.astro";
|
||||
|
||||
export interface Headings {
|
||||
slug: string;
|
||||
text: string;
|
||||
depth: number;
|
||||
}
|
||||
|
||||
export interface Props {
|
||||
content: {
|
||||
title: string;
|
||||
};
|
||||
headings: Headings[];
|
||||
frontmatter?: {
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
}
|
||||
|
||||
const { headings, frontmatter } = Astro.props;
|
||||
---
|
||||
<DocsLayout
|
||||
title={frontmatter!.title}
|
||||
description={frontmatter!.description}
|
||||
headings={headings}
|
||||
>
|
||||
<div class="docs">
|
||||
<div class="docs-content">
|
||||
<hr />
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<TOC mobile={false} headings={headings}/>
|
||||
</DocsLayout>
|
||||
|
||||
<TOCIntersectionObserver/>
|
103
src/layouts/DocsLayout.astro
Normal file
103
src/layouts/DocsLayout.astro
Normal file
|
@ -0,0 +1,103 @@
|
|||
---
|
||||
import { Breadcrumbs } from "astro-breadcrumbs";
|
||||
import "astro-breadcrumbs/breadcrumbs.css";
|
||||
|
||||
import CreateCopyButtons from "@components/hooks/CreateCopyButtons.astro";
|
||||
import PreTheme from "@config/PreTheme.astro";
|
||||
import Header from "@components/Header.astro";
|
||||
import Head from "@config/Head.astro";
|
||||
import Nav from "@components/navigation/sidebars/Nav.astro";
|
||||
import CreateQMLCodeButtons from "@components/hooks/CreateQMLCodeButtons.astro";
|
||||
import "@styles/global.css";
|
||||
import type { ConfigHeading } from "@src/components/navigation/sidebars/types";
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description: string;
|
||||
headings?: ConfigHeading[];
|
||||
}
|
||||
|
||||
const { title, description, headings } = Astro.props;
|
||||
const url = Astro.url.pathname.split("/");
|
||||
|
||||
const customBreadcrumbs = [
|
||||
{
|
||||
index: 0,
|
||||
text: "custom",
|
||||
href: "/",
|
||||
},
|
||||
{
|
||||
text: url[1].slice(0, 1)[0].toUpperCase() + url[1].slice(1),
|
||||
href: `/${url[1]}`,
|
||||
},
|
||||
];
|
||||
|
||||
if (url[2]) {
|
||||
customBreadcrumbs.push({
|
||||
text: url[2].slice(0, 1)[0].toUpperCase() + url[2].slice(1),
|
||||
href: `/${url[1]}/${url[2]}`,
|
||||
});
|
||||
if (url[3]) {
|
||||
customBreadcrumbs.push({
|
||||
text: url[3].slice(0, 1)[0].toUpperCase() + url[3].slice(1),
|
||||
href: `/${url[1]}/${url[2]}/${url[3]}`,
|
||||
});
|
||||
if (url[4]) {
|
||||
customBreadcrumbs.filter((_, index) => index !== 4);
|
||||
customBreadcrumbs.push({
|
||||
text: url[4],
|
||||
href: `/${url[1]}/${url[2]}/${url[3]}/${url[4]}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
---
|
||||
|
||||
<html lang="en" class="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<Head description={description} title={title} />
|
||||
<link rel="canonical" href={Astro.url} />
|
||||
<PreTheme />
|
||||
<CreateCopyButtons />
|
||||
<CreateQMLCodeButtons/>
|
||||
</head>
|
||||
<body class="docslayout">
|
||||
<Header headings={headings}/>
|
||||
<div class="docslayout-root">
|
||||
<Nav mobile={false}/>
|
||||
<div class="docslayout-inner">
|
||||
<Breadcrumbs crumbs={customBreadcrumbs} linkTextFormat="sentence" truncated={true}>
|
||||
<svg
|
||||
slot="index"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
>
|
||||
<title>Home</title>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m219.31 108.68l-80-80a16 16 0 0 0-22.62 0l-80 80A15.87 15.87 0 0 0 32 120v96a8 8 0 0 0 8 8h64a8 8 0 0 0 8-8v-56h32v56a8 8 0 0 0 8 8h64a8 8 0 0 0 8-8v-96a15.87 15.87 0 0 0-4.69-11.32M208 208h-48v-56a8 8 0 0 0-8-8h-48a8 8 0 0 0-8 8v56H48v-88l80-80l80 80Z"
|
||||
></path></svg
|
||||
>
|
||||
<svg
|
||||
slot="separator"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="1em"
|
||||
height="1em"
|
||||
viewBox="0 0 256 256"
|
||||
><path
|
||||
fill="currentColor"
|
||||
d="m181.66 133.66l-80 80a8 8 0 0 1-11.32-11.32L164.69 128L90.34 53.66a8 8 0 0 1 11.32-11.32l80 80a8 8 0 0 1 0 11.32"
|
||||
></path></svg
|
||||
>
|
||||
</Breadcrumbs>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
17
src/pages/docs/configuration/index.mdx
Normal file
17
src/pages/docs/configuration/index.mdx
Normal file
|
@ -0,0 +1,17 @@
|
|||
---
|
||||
layout: "@layouts/ConfigLayout.astro"
|
||||
title: "Configuration"
|
||||
description: "Configuring the shell"
|
||||
---
|
||||
import MD_Title from "@components/MD_Title.tsx"
|
||||
|
||||
# <MD_Title titleVar={1}> {frontmatter.title} </MD_Title>
|
||||
You should start with the [Introduction](./configuration/intro) which will guide you
|
||||
through the basics of QML by creating a simple topbar with a clock.
|
||||
|
||||
From there you can read the [QML Overview](./configuration/qml-overview) to get an overview of
|
||||
the QML language, or jump right into the [Type Reference](/docs/types) to find
|
||||
types you can use in your shell.
|
||||
|
||||
The [quickshell-examples](https://git.outfoxxed.me/outfoxxed/quickshell-examples) repo contains
|
||||
fully working example configurations you can read and modify.
|
844
src/pages/docs/configuration/intro.mdx
Normal file
844
src/pages/docs/configuration/intro.mdx
Normal file
|
@ -0,0 +1,844 @@
|
|||
---
|
||||
layout: "@layouts/ConfigLayout.astro"
|
||||
title: "Introduction"
|
||||
---
|
||||
|
||||
import { DocsCollapsible } from "@components/Collapsible.tsx";
|
||||
import MD_Title from "@components/MD_Title.tsx"
|
||||
|
||||
# <MD_Title titleVar={1}> {frontmatter.title} </MD_Title>
|
||||
|
||||
This page will walk you through the process of creating a simple bar/panel, and
|
||||
introduce you to all the basic concepts involved.
|
||||
|
||||
There are many links to the [QML Overview](../qml-overview)
|
||||
and [Type Reference](/docs/types) which you should follow if you don't
|
||||
fully understand the concepts involved.
|
||||
|
||||
## <MD_Title titleVar={2} client:visible> Shell Files </MD_Title>
|
||||
|
||||
Every quickshell instance starts from a shell root file, conventionally named `shell.qml`.
|
||||
The default path is `~/.config/quickshell/shell.qml`.
|
||||
(where `~/.config` can be substituted with `$XDG_CONFIG_HOME` if present.)
|
||||
|
||||
Each shell file starts with the shell root object. Only one may exist per configuration.
|
||||
|
||||
```qml {filename="~/.config/quickshell/shell.qml"}
|
||||
import Quickshell
|
||||
|
||||
QS_Quickshell_ShellRoot {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The shell root is not a visual element but instead contains all of the visual
|
||||
and non visual objects in your shell. You can have multiple different shells
|
||||
with shared components and different shell roots.
|
||||
|
||||
<DocsCollapsible title="Shell search paths and manifests" client:visible>
|
||||
Quickshell can be launched with configurations in locations other than the default one.
|
||||
|
||||
The `-p` or `--path` option will launch the shell root at the given path.
|
||||
It will also accept folders with a `shell.qml` file in them.
|
||||
It can also be specified via the `QS_CONFIG_PATH` environment variable.
|
||||
|
||||
The `-c` or `--config` option will launch a configuration from the current manifest,
|
||||
or if no manifest is specified, a subfolder of quickshell's base path.
|
||||
It can also be specified via the `QS_CONFIG_NAME` environment variable.
|
||||
|
||||
The base path defaults to `~/.config/quickshell`, but can be changed using
|
||||
the `QS_BASE_PATH` environment variable.
|
||||
|
||||
The `-m` or `--manifest` option specifies the quickshell manifest to read configs
|
||||
from. When used with `-c`, the config will be chosen by name from the manifest.
|
||||
It can also be specified via the `QS_MANIFEST` environment variable.
|
||||
|
||||
The manifest path defaults to `~/.config/quickshell/manifest.conf` and is a list
|
||||
of `name = path` pairs where path can be relative or absolute.
|
||||
Lines starting with `#` are comments.
|
||||
|
||||
```properties
|
||||
# ~/.config/quickshell/manifest.conf
|
||||
myconf1 = myconf
|
||||
myconf2 = ./myconf
|
||||
myconf3 = myconf/shell.nix
|
||||
myconf4 = ~/.config/quickshell/myconf
|
||||
```
|
||||
|
||||
You can use `quickshell --current` to print the current values of any of these
|
||||
options and what set them.
|
||||
|
||||
</DocsCollapsible>
|
||||
|
||||
## <MD_Title titleVar={2}> Creating Windows </MD_Title>
|
||||
|
||||
Quickshell has two main window types available,
|
||||
[PanelWindow](/docs/types/quickshell/panelwindow) for bars and widgets, and
|
||||
[FloatingWindow](/docs/types/quickshell/floatingwindow) for standard desktop windows.
|
||||
|
||||
We'll start with an example:
|
||||
|
||||
```qml
|
||||
import Quickshell // for ShellRoot and PanelWindow
|
||||
import QtQuick // for Text
|
||||
|
||||
QS_Quickshell_ShellRoot {
|
||||
QS_Quickshell_PanelWindow {
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
QT__Text {
|
||||
// center the bar in its parent component (the window)
|
||||
anchors.centerIn: parent
|
||||
|
||||
text: "hello world"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The above example creates a bar/panel on your currently focused monitor with
|
||||
a centered piece of [text](https://doc.qt.io/qt-6/qml-qtquick-text.html). It will also reserve space for itself on your monitor.
|
||||
|
||||
More information about available properties is available in the [type reference](/docs/types/quickshell/panelwindow).
|
||||
|
||||
## <MD_Title titleVar={2}> Running a process </MD_Title>
|
||||
|
||||
Now that we have a piece of text, what if it did something useful?
|
||||
To start with lets make a clock. To get the time we'll use the `date` command.
|
||||
|
||||
We can use a [Process](/docs/types/quickshell.io/process) object to run commands
|
||||
and return their results.
|
||||
|
||||
We'll listen to the [DataStreamParser.read](/docs/types/quickshell.io/datastreamparser/#signal.read)
|
||||
[signal](/docs/configuration/qml-overview/#signals) emitted by
|
||||
[SplitParser](/docs/types/quickshell.io/splitparser/) using a
|
||||
[signal handler](/docs/configuration/qml-overview/#signal-handlers)
|
||||
to update the text on the clock.
|
||||
|
||||
> [!note/Note]
|
||||
> Quickshell live-reloads your code. You can leave it open and edit the
|
||||
> original file. The panel will reload when you save it.
|
||||
|
||||
```qml
|
||||
import Quickshell
|
||||
import Quickshell.Io // for Process
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_ShellRoot {
|
||||
QS_Quickshell_PanelWindow {
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
QT__Text {
|
||||
// give the text an ID we can refer to elsewhere in the file
|
||||
id: clock
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
// create a process management object
|
||||
QS_Quickshell00Io_Process {
|
||||
// the command it will run, every argument is its own string
|
||||
command: ["date"]
|
||||
|
||||
// run the command immediately
|
||||
running: true
|
||||
|
||||
// process the stdout stream using a SplitParser
|
||||
// which returns chunks of output after a delimiter
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
// listen for the read signal, which returns the data that was read
|
||||
// from stdout, then write that data to the clock's text property
|
||||
onRead: data => clock.text = data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## <MD_Title titleVar={2}> Running code at an interval </MD_Title>
|
||||
|
||||
With the above example, your bar should now display the time, but it isn't updating!
|
||||
Let's use a [Timer](https://doc.qt.io/qt-6/qml-qtqml-timer.html) fix that.
|
||||
|
||||
```qml
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_ShellRoot {
|
||||
QS_Quickshell_PanelWindow {
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
QT__Text {
|
||||
id: clock
|
||||
anchors.centerIn: parent
|
||||
|
||||
QS_Quickshell00Io_Process {
|
||||
// give the process object an id so we can talk
|
||||
// about it from the timer
|
||||
id: dateProc
|
||||
|
||||
command: ["date"]
|
||||
running: true
|
||||
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
onRead: data => clock.text = data
|
||||
}
|
||||
}
|
||||
|
||||
// use a timer to rerun the process at an interval
|
||||
QT_qtqml_Timer {
|
||||
// 1000 milliseconds is 1 second
|
||||
interval: 1000
|
||||
|
||||
// start the timer immediately
|
||||
running: true
|
||||
|
||||
// run the timer again when it ends
|
||||
repeat: true
|
||||
|
||||
// when the timer is triggered, set the running property of the
|
||||
// process to true, which reruns it if stopped.
|
||||
onTriggered: dateProc.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## <MD_Title titleVar={2}> Reusable components </MD_Title>
|
||||
|
||||
If you have multiple monitors you might have noticed that your bar
|
||||
is only on one of them. If not, you'll still want to **follow this section
|
||||
to make sure your bar doesn't disappear if your monitor disconnects**.
|
||||
|
||||
We can use a [Variants](/docs/types/quickshell/variants)
|
||||
object to create instances of _non widget items_.
|
||||
(See [Repeater](https://doc.qt.io/qt-6/qml-qtquick-repeater.html) for doing
|
||||
something similar with visual items.)
|
||||
|
||||
The `Variants` type creates instances of a
|
||||
[Component](https://doc.qt.io/qt-6/qml-qtqml-component.html) based on a data model
|
||||
you supply. (A component is a re-usable tree of objects.)
|
||||
|
||||
The most common use of `Variants` in a shell is to create instances of
|
||||
a window (your bar) based on your monitor list (the data model).
|
||||
|
||||
Variants will inject the values in the data model into each new
|
||||
component's `modelData` property, which means we can easily pass each screen
|
||||
to its own component.
|
||||
(See [Window.screen](/docs/types/quickshell/qswindow/#prop.screen).)
|
||||
|
||||
```qml
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_ShellRoot {
|
||||
QS_Quickshell_Variants {
|
||||
model: Quickshell.screens;
|
||||
|
||||
delegate: QT_qtqml_Component {
|
||||
QS_Quickshell_PanelWindow {
|
||||
// the screen from the screens list will be injected into this
|
||||
// property
|
||||
property var modelData
|
||||
|
||||
// we can then set the window's screen to the injected property
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
QT__Text {
|
||||
id: clock
|
||||
anchors.centerIn: parent
|
||||
|
||||
QS_Quickshell00Io_Process {
|
||||
id: dateProc
|
||||
command: ["date"]
|
||||
running: true
|
||||
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
onRead: data => clock.text = data
|
||||
}
|
||||
}
|
||||
|
||||
QT_qtqml_Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: dateProc.running = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="small">
|
||||
See also: [Property
|
||||
Bindings](/docs/configuration/qml-overview/#property-bindings),
|
||||
[Variants.component](/docs/types/quickshell/variants/#prop.component),
|
||||
[Quickshell.screens](/docs/types/quickshell/quickshell/#prop.screens),
|
||||
[Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
|
||||
</span>
|
||||
|
||||
With this example, bars will be created and destroyed as you plug and unplug them,
|
||||
due to the reactive nature of the
|
||||
[Quickshell.screens](/docs/types/quickshell/quickshell/#prop.screens) property.
|
||||
(See: [Reactive Bindings](/docs/configuration/qml-overview/#reactive-bindings).)
|
||||
|
||||
Now there's an important problem you might have noticed: when the window
|
||||
is created multiple times we also make a new Process and Timer. We can fix
|
||||
this by moving the Process and Timer outside of the window.
|
||||
|
||||
> [!caution/Error]
|
||||
> This code will not work correctly.
|
||||
|
||||
```qml
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_ShellRoot {
|
||||
QS_Quickshell_Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
delegate: QT_qtqml_Component {
|
||||
QS_Quickshell_PanelWindow {
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
QT__Text {
|
||||
id: clock
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QS_Quickshell00Io_Process {
|
||||
id: dateProc
|
||||
command: ["date"]
|
||||
running: true
|
||||
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
onRead: data => clock.text = data
|
||||
}
|
||||
}
|
||||
|
||||
QT_qtqml_Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: dateProc.running = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
However there is a problem with naively moving the Process and Timer
|
||||
out of the component.
|
||||
_What about the `clock` that the process references?_
|
||||
|
||||
If you run the above example you'll see something like this in the console every second:
|
||||
|
||||
```
|
||||
file:///home/name/.config/quickshell/shell.qml:33: ReferenceError: clock is not defined
|
||||
file:///home/name/.config/quickshell/shell.qml:33: ReferenceError: clock is not defined
|
||||
file:///home/name/.config/quickshell/shell.qml:33: ReferenceError: clock is not defined
|
||||
file:///home/name/.config/quickshell/shell.qml:33: ReferenceError: clock is not defined
|
||||
file:///home/name/.config/quickshell/shell.qml:33: ReferenceError: clock is not defined
|
||||
```
|
||||
|
||||
This is because the `clock` object, even though it has an ID, cannot be referenced
|
||||
outside of its component. Remember, components can be created _any number of times_,
|
||||
including zero, so `clock` may not exist or there may be more than one, meaning
|
||||
there isn't an object to refer to from here.
|
||||
|
||||
We can fix it with a [Property Definition](/docs/configuration/qml-overview/#property-definitions).
|
||||
|
||||
We can define a property inside of the ShellRoot and reference it from the clock
|
||||
text instead. Due to QML's [Reactive Bindings](/docs/configuration/qml-overview/#reactive-bindings),
|
||||
the clock text will be updated when we update the property for every clock that
|
||||
currently exists.
|
||||
|
||||
```qml
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_ShellRoot {
|
||||
id: root
|
||||
|
||||
// add a property in the root
|
||||
property string time;
|
||||
|
||||
QS_Quickshell_Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
delegate: QT_qtqml_Component {
|
||||
QS_Quickshell_PanelWindow {
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
QT__Text {
|
||||
// remove the id as we don't need it anymore
|
||||
|
||||
anchors.centerIn: parent
|
||||
|
||||
// bind the text to the root's time property
|
||||
text: root.time
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QS_Quickshell00Io_Process {
|
||||
id: dateProc
|
||||
command: ["date"]
|
||||
running: true
|
||||
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
// update the property instead of the clock directly
|
||||
onRead: data => root.time = data
|
||||
}
|
||||
}
|
||||
|
||||
QT_qtqml_Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: dateProc.running = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now we've fixed the problem so there's nothing actually wrong with the
|
||||
above code, but we can make it more concise:
|
||||
|
||||
1. `Component`s can be defined implicitly, meaning we can remove the
|
||||
component wrapping the window and place the window directly into the
|
||||
`delegate` property.
|
||||
2. The [Variants.delegate](/docs/types/quickshell/variants/#prop.delegate)
|
||||
property is a [Default Property](/docs/configuration/qml-overview/#the-default-property),
|
||||
which means we can skip the `delegate:` part of the assignment.
|
||||
We're already using [ShellRoot](/docs/types/quickshell/shellroot/)'s
|
||||
default property to store our Variants, Process, and Timer components
|
||||
among other things.
|
||||
3. The ShellRoot doesn't actually need an `id` property to talk about
|
||||
the time property, as it is the outermost object in the file which
|
||||
has [special scoping rules](/docs/configuration/qml-overview/#property-access-scopes).
|
||||
|
||||
This is what our shell looks like with the above (optional) cleanup:
|
||||
|
||||
```qml
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_ShellRoot {
|
||||
property string time;
|
||||
|
||||
QS_Quickshell_Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
QS_Quickshell_PanelWindow {
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
QT__Text {
|
||||
anchors.centerIn: parent
|
||||
|
||||
// now just time instead of root.time
|
||||
text: time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QS_Quickshell00Io_Process {
|
||||
id: dateProc
|
||||
command: ["date"]
|
||||
running: true
|
||||
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
// now just time instead of root.time
|
||||
onRead: data => time = data
|
||||
}
|
||||
}
|
||||
|
||||
QT_qtqml_Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: dateProc.running = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## <MD_Title titleVar={2}> Multiple files </MD_Title>
|
||||
|
||||
In an example as small as this, it isn't a problem, but as the shell
|
||||
grows it might be preferable to separate it into multiple files.
|
||||
|
||||
To start with, let's move the entire bar into a new file.
|
||||
|
||||
```qml {filename="shell.qml"}
|
||||
import Quickshell
|
||||
|
||||
QS_Quickshell_ShellRoot {
|
||||
Bar {}
|
||||
}
|
||||
```
|
||||
|
||||
```qml {filename="Bar.qml"}
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_Scope {
|
||||
property string time;
|
||||
|
||||
QS_Quickshell_Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
QS_Quickshell_PanelWindow {
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
QT__Text {
|
||||
anchors.centerIn: parent
|
||||
|
||||
// now just time instead of root.time
|
||||
text: time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QS_Quickshell00Io_Process {
|
||||
id: dateProc
|
||||
command: ["date"]
|
||||
running: true
|
||||
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
// now just time instead of root.time
|
||||
onRead: data => time = data
|
||||
}
|
||||
}
|
||||
|
||||
QT_qtqml_Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: dateProc.running = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="small">See also: [Scope](/docs/types/Quickshell/Scope/)</span>
|
||||
|
||||
Any qml file that starts with an uppercase letter can be referenced this way.
|
||||
We can bring in other folders as well using
|
||||
[import statements](/docs/configuration/qml-overview/#explicit-imports).
|
||||
|
||||
Now what about breaking out the clock? This is a bit more complex because
|
||||
the clock component in the bar, as well as the process and timer that
|
||||
make up the actual clock, need to be dealt with.
|
||||
|
||||
To start with, we can move the clock widget to a new file. For now it's just a
|
||||
single `Text` object but the same concepts apply regardless of complexity.
|
||||
|
||||
```qml {filename="ClockWidget.qml"}
|
||||
import QtQuick
|
||||
|
||||
QT__Text {
|
||||
// A property the creator of this type is required to set.
|
||||
// Note that we could just set `text` instead, but don't because your
|
||||
// clock probably will not be this simple.
|
||||
required property string time
|
||||
|
||||
text: time
|
||||
}
|
||||
```
|
||||
|
||||
```qml {filename="Bar.qml"}
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_Scope {
|
||||
id: root
|
||||
property string time;
|
||||
|
||||
QS_Quickshell_Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
QS_Quickshell_PanelWindow {
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
// the ClockWidget type we just created
|
||||
ClockWidget {
|
||||
anchors.centerIn: parent
|
||||
// Warning: setting `time: time` will bind time to itself which is not what we want
|
||||
time: root.time
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QS_Quickshell00Io_Process {
|
||||
id: dateProc
|
||||
command: ["date"]
|
||||
running: true
|
||||
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
onRead: data => time = data
|
||||
}
|
||||
}
|
||||
|
||||
QT_qtqml_Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: dateProc.running = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
While this example is larger than what we had before, we can now expand
|
||||
on the clock widget without cluttering the bar file.
|
||||
|
||||
Let's deal with the clock's update logic now:
|
||||
|
||||
```qml {filename="Time.qml"}
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_Scope {
|
||||
property string time;
|
||||
|
||||
QS_Quickshell00Io_Process {
|
||||
id: dateProc
|
||||
command: ["date"]
|
||||
running: true
|
||||
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
onRead: data => time = data
|
||||
}
|
||||
}
|
||||
|
||||
QT_qtqml_Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: dateProc.running = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```qml {filename="Bar.qml"}
|
||||
import Quickshell
|
||||
|
||||
QS_Quickshell_Scope {
|
||||
// the Time type we just created
|
||||
Time { id: timeSource }
|
||||
|
||||
QS_Quickshell_Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
QS_Quickshell_PanelWindow {
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
ClockWidget {
|
||||
anchors.centerIn: parent
|
||||
// now using the time from timeSource
|
||||
time: timeSource.time
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## <MD_Title titleVar={2}> Singletons </MD_Title>
|
||||
|
||||
Now you might be thinking, why do we need the `Time` type in
|
||||
our bar file, and the answer is we don't. We can make `Time`
|
||||
a [Singleton](/docs/configuration/qml-overview/#singletons).
|
||||
|
||||
A singleton object has only one instance, and is accessible from
|
||||
any scope.
|
||||
|
||||
```qml {filename="Time.qml"}
|
||||
// with this line our type becomes a singleton
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
// your singletons should always have Singleton as the type
|
||||
QS_Quickshell_Singleton {
|
||||
property string time
|
||||
|
||||
QS_Quickshell00Io_Process {
|
||||
id: dateProc
|
||||
command: ["date"]
|
||||
running: true
|
||||
|
||||
stdout: QS_Quickshell00Io_SplitParser {
|
||||
onRead: data => time = data
|
||||
}
|
||||
}
|
||||
|
||||
QT_qtqml_Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: dateProc.running = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```qml {filename="ClockWidget.qml"}
|
||||
import QtQuick
|
||||
|
||||
QT__Text {
|
||||
// we no longer need time as an input
|
||||
|
||||
// directly access the time property from the Time singleton
|
||||
text: Time.time
|
||||
}
|
||||
```
|
||||
|
||||
```qml {filename="Bar.qml"}
|
||||
import Quickshell
|
||||
|
||||
QS_Quickshell_Scope {
|
||||
// no more time object
|
||||
|
||||
QS_Quickshell_Variants {
|
||||
model: Quickshell.screens
|
||||
|
||||
QS_Quickshell_PanelWindow {
|
||||
property var modelData
|
||||
screen: modelData
|
||||
|
||||
anchors {
|
||||
top: true
|
||||
left: true
|
||||
right: true
|
||||
}
|
||||
|
||||
height: 30
|
||||
|
||||
ClockWidget {
|
||||
anchors.centerIn: parent
|
||||
|
||||
// no more time binding
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## <MD_Title titleVar={2}> JavaScript APIs </MD_Title>
|
||||
|
||||
In addition to calling external processes, a [limited set of javascript interfaces] is available.
|
||||
We can use this to improve our clock by using the [Date API] instead of calling `date`.
|
||||
|
||||
[limited set of javascript interfaces]: https://doc.qt.io/qt-6/qtqml-javascript-functionlist.html
|
||||
[Date API]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date
|
||||
|
||||
```qml {filename="Time.qml"}
|
||||
pragma Singleton
|
||||
|
||||
import Quickshell
|
||||
import Quickshell.Io
|
||||
import QtQuick
|
||||
|
||||
QS_Quickshell_Singleton {
|
||||
property var date: new Date()
|
||||
property string time: date.toLocaleString(Qt.locale())
|
||||
|
||||
QT_qtqml_Timer {
|
||||
interval: 1000
|
||||
running: true
|
||||
repeat: true
|
||||
onTriggered: date = new Date()
|
||||
}
|
||||
}
|
||||
```
|
117
src/pages/docs/configuration/positioning.mdx
Normal file
117
src/pages/docs/configuration/positioning.mdx
Normal file
|
@ -0,0 +1,117 @@
|
|||
---
|
||||
layout: "@layouts/ConfigLayout.astro"
|
||||
title: "Positioning"
|
||||
---
|
||||
import MD_Title from "@components/MD_Title.tsx"
|
||||
|
||||
# <MD_Title titleVar={1}> {frontmatter.title} </MD_Title>
|
||||
|
||||
QtQuick has multiple ways to position components. This page has instructions for where and how
|
||||
to use them.
|
||||
|
||||
## <MD_Title titleVar={2}> Anchors </MD_Title>
|
||||
|
||||
Anchors can be used to position components relative to another neighboring component.
|
||||
It is faster than [manual positioning](#manual-positioning) and covers a lot of simple
|
||||
use cases.
|
||||
|
||||
The [Qt Documentation: Positioning with Anchors](https://doc.qt.io/qt-6/qtquick-positioning-anchors.html)
|
||||
page has comprehensive documentation of anchors.
|
||||
|
||||
## <MD_Title titleVar={2}> Layouts </MD_Title>
|
||||
|
||||
Layouts are useful when you have many components that need to be positioned relative to
|
||||
eachother such as a list.
|
||||
|
||||
The [Qt Documentation: Layouts Overview](https://doc.qt.io/qt-6/qtquicklayouts-overview.html)
|
||||
page has good documentation of the basic layout types and how to use them.
|
||||
|
||||
> [!note/Note:]
|
||||
> Layouts by default have a nonzero spacing.
|
||||
|
||||
## <MD_Title titleVar={2}> Manual Positioning </MD_Title>
|
||||
|
||||
If layouts and anchors can't easily fulfill your usecase, you can also manually position and size
|
||||
components by setting their `x`, `y`, `width` and `height` properties, which are relative to
|
||||
the parent component.
|
||||
|
||||
This example puts a 100x100px blue rectangle at x=20,y=40 in the parent item. Ensure the size
|
||||
of the parent is large enough for its content or positioning based on them will break.
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
// make sure the component is large enough to fit its children
|
||||
implicitWidth: childrenRect.width
|
||||
implicitHeight: childrenRect.height
|
||||
|
||||
QT__Rectangle {
|
||||
color: "blue"
|
||||
x: 20
|
||||
y: 40
|
||||
width: 100
|
||||
height: 100
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## <MD_Title titleVar={2}> Notes </MD_Title>
|
||||
|
||||
### <MD_Title titleVar={3}> Component Size </MD_Title>
|
||||
|
||||
The [Item.implicitHeight] and [Item.implicitWidth] properties control the _base size_ of a
|
||||
component, before layouts are applied. These properties are _not_ the same as
|
||||
[Item.height] and [Item.width] which are the final size of the component.
|
||||
You should nearly always use the implicit size properties when creating a component,
|
||||
however using the normal width and height properties is fine if you know an
|
||||
item will never go in a layout.
|
||||
|
||||
[Item.height]: https://doc.qt.io/qt-6/qml-qtquick-item.html#height-prop
|
||||
[Item.width]: https://doc.qt.io/qt-6/qml-qtquick-item.html#width-prop
|
||||
[Item.implicitHeight]: https://doc.qt.io/qt-6/qml-qtquick-item.html#implicitHeight-prop
|
||||
[Item.implicitWidth]: https://doc.qt.io/qt-6/qml-qtquick-item.html#implicitWidth-prop
|
||||
|
||||
This example component puts a colored rectangle behind some text, and will act the same
|
||||
way in a layout as the text by itself.
|
||||
|
||||
```qml {filename="TextWithBkgColor.qml"}
|
||||
QT__Rectangle {
|
||||
implicitWidth: text.implicitWidth
|
||||
implicitHeight: text.implicitHeight
|
||||
|
||||
QT__Text {
|
||||
id: text
|
||||
text: "hello!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you want to size your component based on multiple others or use any other math you can.
|
||||
|
||||
```qml {filename="PaddedTexts.qml"}
|
||||
QT__Item {
|
||||
// width of both texts plus 5
|
||||
implicitWidth: text1.implicitWidth + text2.implicitWidth + 5
|
||||
// max height of both texts plus 5
|
||||
implicitHeight: Math.min(text1.implicitHeight, text2.implicitHeight) + 5
|
||||
|
||||
QT__Text {
|
||||
id: text1
|
||||
text: "text1"
|
||||
}
|
||||
|
||||
QT__Text {
|
||||
id: text2
|
||||
anchors.left: text1.left
|
||||
text: "text2"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <MD_Title titleVar={3}> Coordinate space </MD_Title>
|
||||
|
||||
You should always position or size components relative to the closest possible
|
||||
parent. Often this is just the `parent` property.
|
||||
|
||||
Refrain from using things like the size of your screen to size a component,
|
||||
as this will break as soon as anything up the component hierarchy changes, such
|
||||
as adding padding to a bar.
|
888
src/pages/docs/configuration/qml-overview.mdx
Normal file
888
src/pages/docs/configuration/qml-overview.mdx
Normal file
|
@ -0,0 +1,888 @@
|
|||
---
|
||||
layout: "@layouts/ConfigLayout.astro"
|
||||
title: "QML Overview"
|
||||
---
|
||||
import MD_Title from "@components/MD_Title.tsx"
|
||||
import { DocsCollapsible } from "@components/Collapsible.tsx";
|
||||
|
||||
# <MD_Title titleVar={1}> {frontmatter.title} </MD_Title>
|
||||
|
||||
Quickshell is configured using the Qt Modeling Language, or QML.
|
||||
This page explains what you need to know about QML to start using quickshell.
|
||||
|
||||
<span class="small">
|
||||
See also: [Qt Documentation: QML
|
||||
Tutorial](https://doc.qt.io/qt-6/qml-tutorial.html)
|
||||
</span>
|
||||
|
||||
## <MD_Title titleVar={2}> Structure </MD_Title>
|
||||
|
||||
Below is a QML document showing most of the syntax.
|
||||
Keep it in mind as you read the detailed descriptions below.
|
||||
|
||||
> [!note/Notes:]
|
||||
>
|
||||
> - Semicolons are permitted basically everywhere, and recommended in
|
||||
> functions and expressions.
|
||||
> - While types can often be elided, we recommend you use them where
|
||||
> possible to catch problems early instead of running into them unexpectedly later on.
|
||||
|
||||
```qml
|
||||
// QML Import statement
|
||||
import QtQuick 6.0
|
||||
|
||||
// Javascript import statement
|
||||
import "myjs.js" as MyJs
|
||||
|
||||
// Root Object
|
||||
QT__Item {
|
||||
// Id assignment
|
||||
|
||||
id: root
|
||||
// Property declaration
|
||||
property int myProp: 5;
|
||||
|
||||
// Property binding
|
||||
width: 100
|
||||
|
||||
// Property binding
|
||||
height: width
|
||||
|
||||
// Multiline property binding
|
||||
prop: {
|
||||
// ...
|
||||
5
|
||||
}
|
||||
|
||||
// Object assigned to a property
|
||||
objProp: Object {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Object assigned to the parent's default property
|
||||
AnotherObject {
|
||||
// ...
|
||||
}
|
||||
|
||||
// Signal declaration
|
||||
signal foo(bar: int)
|
||||
|
||||
// Signal handler
|
||||
onSignal: console.log("received signal!")
|
||||
|
||||
// Property change signal handler
|
||||
onWidthChanged: console.log(`width is now ${width}!`)
|
||||
|
||||
// Multiline signal handler
|
||||
onOtherSignal: {
|
||||
console.log("received other signal!");
|
||||
console.log(`5 * 2 is ${dub(5)}`);
|
||||
// ...
|
||||
}
|
||||
|
||||
// Attached property signal handler
|
||||
Component.onCompleted: MyJs.myfunction()
|
||||
|
||||
// Function
|
||||
function dub(x: int): int {
|
||||
return x * 2
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### <MD_Title titleVar={3}> Imports </MD_Title>
|
||||
|
||||
#### <MD_Title titleVar={4}> Manual imports </MD_Title>
|
||||
|
||||
Every QML File begins with a list of imports.
|
||||
Import statements tell the QML engine where
|
||||
to look for types you can create [objects](#objects) from.
|
||||
|
||||
A module import statement looks like this:
|
||||
|
||||
```qml
|
||||
import <Module> [Major.Minor] [as <Namespace>]
|
||||
```
|
||||
|
||||
- `Module` is the name of the module you want to import, such as `QtQuick`.
|
||||
- `Major.Minor` is the version of the module you want to import.
|
||||
- `Namespace` is an optional namespace to import types from the module under.
|
||||
|
||||
A subfolder import statement looks like this:
|
||||
|
||||
```qml
|
||||
import "<directory>" [as <Namespace>]
|
||||
```
|
||||
|
||||
- `directory` is the directory to import, relative to the current file.
|
||||
- `Namespace` is an optional namespace to import types from the folder under.
|
||||
|
||||
A javascript import statement looks like this:
|
||||
|
||||
```qml
|
||||
import "<filename>" as <Namespace>
|
||||
```
|
||||
|
||||
- `filename` is the name of the javascript file to import.
|
||||
- `Namespace` is the namespace functions and variables from the javascript
|
||||
file will be made available under.
|
||||
|
||||
Note: All _Module_ and _Namespace_ names must start with an uppercase letter.
|
||||
Attempting to use a lowercase namespace is an error.
|
||||
|
||||
##### <MD_Title titleVar={5}> Examples </MD_Title>
|
||||
|
||||
```qml
|
||||
import QtQuick
|
||||
import QtQuick.Controls 6.0
|
||||
import Quickshell as QS
|
||||
import QtQuick.Layouts 6.0 as L
|
||||
import "jsfile.js" as JsFile
|
||||
```
|
||||
|
||||
<DocsCollapsible title="When no module version" client:visible>
|
||||
|
||||
By default, when no module version is requested, the QML engine will pick
|
||||
the latest available version of the module. Requesting a specific version
|
||||
can help ensure you get a specific version of the module's types, and as a
|
||||
result your code doesn't break across Qt or quickshell updates.
|
||||
|
||||
While Qt's types usually don't majorly change across versions, quickshell's
|
||||
are much more likely to break. To put off dealing with the breakage we suggest
|
||||
specifying a version at least when importing quickshell modules.
|
||||
|
||||
</DocsCollapsible>
|
||||
|
||||
<span class="small">
|
||||
[Qt Documentation: Import
|
||||
syntax](https://doc.qt.io/qt-6/qtqml-syntax-imports.html)
|
||||
</span>
|
||||
|
||||
#### <MD_Title titleVar={4}> Implicit imports </MD_Title>
|
||||
|
||||
The QML engine will automatically import any [types](#creating-types) in neighboring files
|
||||
with names that start with an uppercase letter.
|
||||
|
||||
```
|
||||
root
|
||||
|-MyButton.qml
|
||||
|-shell.qml
|
||||
```
|
||||
|
||||
In this example, `MyButton` will automatically be imported as a type usable from shell.qml
|
||||
or any other neighboring files.
|
||||
|
||||
### <MD_Title titleVar={3}> Objects </MD_Title>
|
||||
|
||||
Objects are instances of a type from an imported module.
|
||||
The name of an object must start with an uppercase letter.
|
||||
This will always distinguish an object from a property.
|
||||
|
||||
An object looks like this:
|
||||
|
||||
```qml
|
||||
Name {
|
||||
id: foo
|
||||
// properties, functions, signals, etc...
|
||||
}
|
||||
```
|
||||
|
||||
Every object can contain [properties](#properties), [functions](#functions),
|
||||
and [signals](#signals). You can find out what properties are available for a type
|
||||
by looking it up in the [Type Reference](/docs/types/).
|
||||
|
||||
#### <MD_Title titleVar={4}> Properties </MD_Title>
|
||||
|
||||
Every object may have any number of property assignments (only one per specific property).
|
||||
Each assignment binds the named property to the given expression.
|
||||
|
||||
##### <MD_Title titleVar={5}> Property bindings </MD_Title>
|
||||
|
||||
Expressions are snippets of javascript code assigned to a property. The last (or only) line
|
||||
can be the return value, or an explicit return statement (multiline expressions only) can be used.
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
// simple expression
|
||||
property: 5
|
||||
|
||||
// complex expression
|
||||
property: 5 * 20 + this.otherProperty
|
||||
|
||||
// multiline expression
|
||||
property: {
|
||||
const foo = 5;
|
||||
const bar = 10;
|
||||
foo * bar
|
||||
}
|
||||
|
||||
// multiline expression with return
|
||||
property: {
|
||||
// ...
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Semicolons are optional and allowed on any line of a single or multiline expression,
|
||||
including the last line.
|
||||
|
||||
All property bindings are [_reactive_](#reactive-bindings), which means when any property the expression depends
|
||||
on is updated, the expression is re-evaluated and the property is updated.
|
||||
|
||||
<span class="small">See: [Reactive bindings](#reactive-bindings)</span>
|
||||
|
||||
Note that it is an error to try to assign to a property that does not exist.
|
||||
(See: [property definitions](#property-definitions))
|
||||
|
||||
##### <MD_Title titleVar={5}> Property definitions </MD_Title>
|
||||
|
||||
Properties can be defined inside of objects with the following syntax:
|
||||
|
||||
```qml
|
||||
[required] [readonly] [default] property <type> <name>[: binding]
|
||||
```
|
||||
|
||||
- `required` forces users of this type to assign this property. See [Creating Types](#creating-types) for details.
|
||||
- `readonly` makes the property not assignable. Its binding will still be [reactive](#reactive-bindings).
|
||||
- `default` makes the property the [default property](#the-default-property) of this type.
|
||||
- `type` is the type of the property. You can use `var` if you don't know or don't care but be aware that `var` will
|
||||
allow any value type.
|
||||
- `name` is the name that the property is known as. It cannot start with an uppercase letter.
|
||||
- `binding` is the property binding. See [Property bindings](#property-bindings) for details.
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
// normal property
|
||||
property int foo: 3
|
||||
|
||||
// readonly property
|
||||
readonly property string bar: "hi!"
|
||||
|
||||
// bound property
|
||||
property var things: [ "foo", "bar" ]
|
||||
}
|
||||
```
|
||||
|
||||
Defining a property with the same name as one provided by the current object will override
|
||||
the property of the type it is derived from in the current context.
|
||||
|
||||
##### <MD_Title titleVar={5}> The default property </MD_Title>
|
||||
|
||||
Types can have a _default property_ which must accept either an object or a list of objects.
|
||||
|
||||
The default property will allow you to assign a value to it without using the name of the property:
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
// normal property
|
||||
foo: 3
|
||||
|
||||
// this item is assigned to the outer object's default property
|
||||
QT__Item {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the default property is a list, you can put multiple objects into it the same way as you
|
||||
would put a single object in:
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
// normal property
|
||||
foo: 3
|
||||
|
||||
// this item is assigned to the outer object's default property
|
||||
QT__Item {
|
||||
}
|
||||
|
||||
// this one is too
|
||||
QT__Item {
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### <MD_Title titleVar={5}> The `id` property </MD_Title>
|
||||
|
||||
Every object has a special property called `id` that can be assigned to give
|
||||
the object a name it can be referred to throughout the current file. The id must be lowercase.
|
||||
|
||||
```qml
|
||||
QT_qtquick11layouts_ColumnLayout {
|
||||
QT__Text {
|
||||
id: text
|
||||
text: "Hello World!"
|
||||
}
|
||||
|
||||
QT_qtquick11controls_Button {
|
||||
text: "Make the text red";
|
||||
onClicked: text.color = "red";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<DocsCollapsible title="The `id` property compared to normal properties" client:visible>
|
||||
|
||||
The `id` property isn't really a property, and doesn't do anything other than
|
||||
expose the object to the current file. It is only called a property because it
|
||||
uses very similar syntax to one, and is the only exception to standard property
|
||||
definition rules. The name `id` is always reserved for the id property.
|
||||
|
||||
</DocsCollapsible>
|
||||
|
||||
##### <MD_Title titleVar={5}> Property access scopes </MD_Title>
|
||||
|
||||
Properties are "in scope" and usable in two cases.
|
||||
|
||||
1. They are defined for current type.
|
||||
2. They are defined for the root type in the current file.
|
||||
|
||||
You can access the properties of any object by setting its [id property](#the-id-property),
|
||||
or make sure the property you are accessing is from the current object using `this`.
|
||||
|
||||
The `parent` property is also defined for all objects, but may not always point to what it
|
||||
looks like it should. Use the `id` property if `parent` does not do what you want.
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
property string rootDefinition
|
||||
|
||||
QT__Item {
|
||||
id: mid
|
||||
property string midDefinition
|
||||
|
||||
QT__Text {
|
||||
property string innerDefinition
|
||||
|
||||
// legal - innerDefinition is defined on the current object
|
||||
text: innerDefinition
|
||||
|
||||
// legal - innerDefinition is accessed via `this` to refer to the current object
|
||||
text: this.innerDefinition
|
||||
|
||||
// legal - width is defined for Text
|
||||
text: width
|
||||
|
||||
// legal - rootDefinition is defined on the root object
|
||||
text: rootDefinition
|
||||
|
||||
// illegal - midDefinition is not defined on the root or current object
|
||||
text: midDefinition
|
||||
|
||||
// legal - midDefinition is accessed via `mid`'s id.
|
||||
text: mid.midDefinition
|
||||
|
||||
// legal - midDefinition is accessed via `parent`
|
||||
text: parent.midDefinition
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="small">
|
||||
[Qt Documentation: Scope and Naming
|
||||
Resolution](https://doc.qt.io/qt-6/qtqml-documents-scope.html)
|
||||
</span>
|
||||
|
||||
#### <MD_Title titleVar={4}> Functions </MD_Title>
|
||||
|
||||
Functions in QML can be declared everywhere [properties](#properties) can, and follow
|
||||
the same [scoping rules](#property-access-scopes).
|
||||
|
||||
Function definition syntax:
|
||||
|
||||
```qml
|
||||
function <name>(<paramname>[: <type>][, ...])[: returntype] {
|
||||
// multiline expression (note that `return` is required)
|
||||
}
|
||||
```
|
||||
|
||||
Functions can be invoked in expressions. Expression reactivity carries through
|
||||
functions, meaning if one of the properties a function depends on is re-evaluated,
|
||||
every expression depending on the function is also re-evaluated.
|
||||
|
||||
```qml
|
||||
QT_qtquick11layouts_ColumnLayout {
|
||||
property int clicks: 0
|
||||
|
||||
function makeClicksLabel(): string {
|
||||
return "the button has been clicked " + clicks + " times!";
|
||||
}
|
||||
|
||||
QT_qtquick11controls_Button {
|
||||
text: "click me"
|
||||
onClicked: clicks += 1
|
||||
}
|
||||
|
||||
QT__Text {
|
||||
text: makeClicksLabel()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, every time the button is clicked, the label's count increases
|
||||
by one, as `clicks` is changed, which triggers a re-evaluation of `text` through
|
||||
`makeClicksLabel`.
|
||||
|
||||
##### <MD_Title titleVar={5}> Lambdas </MD_Title>
|
||||
|
||||
Functions can also be values, and you can assign them to properties or pass them to
|
||||
other functions (callbacks). There is a shorter way to write these functions, known
|
||||
as lambdas.
|
||||
|
||||
Lambda syntax:
|
||||
|
||||
```qml
|
||||
<params> => <expression>
|
||||
|
||||
// params can take the following forms:
|
||||
() => ... // 0 parameters
|
||||
<name> => ... // 1 parameter
|
||||
(<name>[, ...]) => ... // 1+ parameters
|
||||
|
||||
// the expression can be either a single or multiline expression.
|
||||
... => <result>
|
||||
... => {
|
||||
return <result>;
|
||||
}
|
||||
```
|
||||
|
||||
Assigning functions to properties:
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
// using functions
|
||||
function dub(number: int): int { return number * 2; }
|
||||
property var operation: dub
|
||||
|
||||
// using lambdas
|
||||
property var operation: number => number * 2
|
||||
}
|
||||
```
|
||||
|
||||
An overcomplicated click counter using a lambda callback:
|
||||
|
||||
```qml
|
||||
QT_qtquick11layouts_ColumnLayout {
|
||||
property int clicks: 0
|
||||
|
||||
function incrementAndCall(callback) {
|
||||
clicks += 1;
|
||||
callback(clicks);
|
||||
}
|
||||
|
||||
QT_qtquick11controls_Button {
|
||||
text: "click me"
|
||||
onClicked: incrementAndCall(clicks => {
|
||||
label.text = `the button was clicked ${clicks} time(s)!`;
|
||||
})
|
||||
}
|
||||
|
||||
QT__Text {
|
||||
id: label
|
||||
text: "the button has not been clicked"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### <MD_Title titleVar={4}> Signals </MD_Title>
|
||||
|
||||
A signal is basically an event emitter you can connect to and receive updates from.
|
||||
They can be declared everywhere [properties](#properties) and [functions](#functions)
|
||||
can, and follow the same [scoping rules](#property-access-scopes).
|
||||
|
||||
<span class="small">
|
||||
[Qt Documentation: Signal and Handler Event
|
||||
System](https://doc.qt.io/qt-6/qtqml-syntax-signals.html)
|
||||
</span>
|
||||
|
||||
##### <MD_Title titleVar={5}> Signal definitions </MD_Title>
|
||||
|
||||
A signal can be explicitly defined with the following syntax:
|
||||
|
||||
```qml
|
||||
signal <name>(<paramname>: <type>[, ...])
|
||||
```
|
||||
|
||||
##### <MD_Title titleVar={5}> Making connections </MD_Title>
|
||||
|
||||
Signals all have a `connect(<function>)` method which invokes the given function
|
||||
or signal when the signal is emitted.
|
||||
|
||||
```qml
|
||||
QT_qtquick11layouts_ColumnLayout {
|
||||
property int clicks: 0
|
||||
|
||||
function updateText() {
|
||||
clicks += 1;
|
||||
label.text = `the button has been clicked ${clicks} times!`;
|
||||
}
|
||||
|
||||
QT_qtquick11controls_Button {
|
||||
id: button
|
||||
text: "click me"
|
||||
}
|
||||
|
||||
QT__Text {
|
||||
id: label
|
||||
text: "the button has not been clicked"
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
button.clicked.connect(updateText)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<span class="small">
|
||||
`Component.onCompleted` will be addressed later in [Attached
|
||||
Properties](#attached-properties) but for now just know that it runs
|
||||
immediately once the object is fully initialized.
|
||||
</span>
|
||||
|
||||
When the button is clicked, the button emits the `clicked` signal which we connected to
|
||||
`updateText`. The signal then invokes `updateText` which updates the counter and the
|
||||
text on the label.
|
||||
|
||||
##### <MD_Title titleVar={5}> Signal handlers </MD_Title>
|
||||
|
||||
Signal handlers are a more concise way to make a connections, and prior examples have used them.
|
||||
|
||||
When creating an object, for every signal present on its type there is a corresponding `on<Signal>`
|
||||
property implicitly defined which can be set to a function. (Note that the first letter of the
|
||||
signal's name it capitalized.)
|
||||
|
||||
Below is the same example as in [Making Connections](#making-connections),
|
||||
this time using the implicit signal handler property to handle `button.clicked`.
|
||||
|
||||
```qml
|
||||
QT_qtquick11layouts_ColumnLayout {
|
||||
property int clicks: 0
|
||||
|
||||
function updateText() {
|
||||
clicks += 1;
|
||||
label.text = `the button has been clicked ${clicks} times!`;
|
||||
}
|
||||
|
||||
QT_qtquick11controls_Button {
|
||||
text: "click me"
|
||||
onClicked: updateText()
|
||||
}
|
||||
|
||||
QT__Text {
|
||||
id: label
|
||||
text: "the button has not been clicked"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### <MD_Title titleVar={5}> Indirect signal handlers </MD_Title>
|
||||
|
||||
When it is not possible or not convenient to directly define a signal handler, before resorting
|
||||
to `.connect`ing the properties, a [Connections] object can be used to access them.
|
||||
|
||||
This is especially useful to connect to signals of singletons.
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
QT_qtquick11controls_Button {
|
||||
id: myButton
|
||||
text "click me"
|
||||
}
|
||||
|
||||
QT_qtqml_Connections {
|
||||
target: myButton
|
||||
|
||||
function onClicked() {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### <MD_Title titleVar={5}> Property change signals </MD_Title>
|
||||
|
||||
Every property has an associated signal, which powers QML's [reactive bindings](#reactive-bindings).
|
||||
The signal is named `<propertyname>Changed` and works exactly the same as any other signal.
|
||||
|
||||
Whenever the property is re-evaluated, its change signal is emitted. This is used internally
|
||||
to update dependent properties, but can be directly used, usually with a signal handler.
|
||||
|
||||
```qml
|
||||
QT_qtquick11layouts_ColumnLayout {
|
||||
CheckBox {
|
||||
text: "check me"
|
||||
|
||||
onCheckStateChanged: {
|
||||
label.text = labelText(checkState == Qt.Checked);
|
||||
}
|
||||
}
|
||||
|
||||
QT__Text {
|
||||
id: label
|
||||
text: labelText(false)
|
||||
}
|
||||
|
||||
function labelText(checked): string {
|
||||
return `the checkbox is checked: ${checked}`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example we listen for the `checkState` property of the CheckBox changing
|
||||
using its change signal, `checkStateChanged` with the signal handler `onCheckStateChanged`.
|
||||
|
||||
Since text is also a property we can do the same thing more concisely:
|
||||
|
||||
```qml
|
||||
QT_qtquick11layouts_ColumnLayout {
|
||||
CheckBox {
|
||||
id: checkbox
|
||||
text: "check me"
|
||||
}
|
||||
|
||||
QT__Text {
|
||||
id: label
|
||||
text: labelText(checkbox.checkState == Qt.Checked)
|
||||
}
|
||||
|
||||
function labelText(checked): string {
|
||||
return `the checkbox is checked: ${checked}`;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
And the function can also be inlined to an expression:
|
||||
|
||||
```qml
|
||||
QT_qtquick11layouts_ColumnLayout {
|
||||
CheckBox {
|
||||
id: checkbox
|
||||
text: "check me"
|
||||
}
|
||||
|
||||
QT__Text {
|
||||
id: label
|
||||
text: {
|
||||
const checked = checkbox.checkState == Qt.Checked;
|
||||
return `the checkbox is checked: ${checked}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also remove the return statement if you wish.
|
||||
|
||||
##### <MD_Title titleVar={5}> Attached objects </MD_Title>
|
||||
|
||||
Attached objects are additional objects that can be associated with an object
|
||||
as decided by internal library code. The documentation for a type will
|
||||
tell you if it can be used as an attached object and how.
|
||||
|
||||
Attached objects are accessed in the form `<Typename>.<member>` and can have
|
||||
properties, functions and signals.
|
||||
|
||||
A good example is the [Component](https://doc.qt.io/qt-6/qml-qtqml-component.html) type,
|
||||
which is attached to every object and often used to run code when an object initializes.
|
||||
|
||||
```qml
|
||||
QT__Text {
|
||||
Component.onCompleted: {
|
||||
text = "hello!"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the text property is set inside the `Component.onCompleted` attached signal handler.
|
||||
|
||||
#### <MD_Title titleVar={4}> Creating types </MD_Title>
|
||||
|
||||
Every QML file with an uppercase name is implicitly a type, and can be used from
|
||||
neighboring files or imported (See [Imports](#imports).)
|
||||
|
||||
A type definition is just a normal object. All properties defined for the root object
|
||||
are visible to the consumer of the type. Objects identified by [id properties](#the-id-property)
|
||||
are not visible outside the file.
|
||||
|
||||
```qml
|
||||
// MyText.qml
|
||||
QT__Rectangle {
|
||||
required property string text
|
||||
|
||||
color: "red"
|
||||
implicitWidth: textObj.implicitWidth
|
||||
implicitHeight: textObj.implicitHeight
|
||||
|
||||
QT__Text {
|
||||
id: textObj
|
||||
anchors.fill: parent
|
||||
text: parent.text
|
||||
}
|
||||
}
|
||||
|
||||
// AnotherComponent.qml
|
||||
QT__Item {
|
||||
MyText {
|
||||
// The `text` property of `MyText` is required, so we must set it.
|
||||
text: "Hello World!"
|
||||
|
||||
// `anchors` is a property of `Item` which `Rectangle` subclasses,
|
||||
// so it is available on MyText.
|
||||
anchors.centerIn: parent
|
||||
|
||||
// `color` is a property of `Rectangle`. Even though MyText sets it
|
||||
// to "red", we can override it here.
|
||||
color: "blue"
|
||||
|
||||
// `textObj` is has an `id` within MyText.qml but is not a property
|
||||
// so we cannot access it.
|
||||
textObj.color: "red" // illegal
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### <MD_Title titleVar={5}> Singletons </MD_Title>
|
||||
|
||||
QML Types can be easily made into a singleton, meaning there is only one instance
|
||||
of the type.
|
||||
|
||||
To make a type a singleton, put `pragma Singleton` at the top of the file.
|
||||
To ensure it behaves correctly with quickshell you should also make
|
||||
[Singleton](/docs/types/quickshell/singleton) the root item of your type.
|
||||
|
||||
```qml
|
||||
pragma Singleton
|
||||
import ...
|
||||
|
||||
QS_Quickshell_Singleton {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
once a type is a singleton, its members can be accessed by name from neighboring
|
||||
files.
|
||||
|
||||
## <MD_Title titleVar={2}> Concepts </MD_Title>
|
||||
|
||||
### <MD_Title titleVar={3}> Reactive bindings </MD_Title>
|
||||
|
||||
<span class="small">
|
||||
This section assumes knowledge of: [Properties](#properties),
|
||||
[Signals](#signals), and [Functions](#functions). See also the [Qt
|
||||
documentation](https://doc.qt.io/qt-6/qtqml-syntax-propertybinding.html).
|
||||
</span>
|
||||
|
||||
Reactivity is when a property is updated based on updates to another property.
|
||||
Every time one of the properties in a binding change, the binding is re-evaluated
|
||||
and the bound property takes the new result. Any bindings that depend on that property
|
||||
are then re-evaluated and so forth.
|
||||
|
||||
Bindings can be created in two different ways:
|
||||
|
||||
##### <MD_Title titleVar={5}> Automatic bindings </MD_Title>
|
||||
|
||||
A reactive binding occurs automatically when you use one or more properties in the definition
|
||||
of another property. .
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
property int clicks: 0
|
||||
|
||||
QT_qtquick11controls_Button {
|
||||
text: `clicks: ${clicks}`
|
||||
onClicked: clicks += 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, the button's `text` property is re-evaluated every time the button is clicked, because
|
||||
the `clicks` property has changed.
|
||||
|
||||
###### <MD_Title titleVar={6}> Avoiding creation </MD_Title>
|
||||
|
||||
To avoid creating a binding, do not use any other properties in the definition of a property.
|
||||
|
||||
You can use the `Component.onCompleted` signal to set a value using a property without creating a binding,
|
||||
as assignments to properties do not create binding.
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
property string theProperty: "initial value"
|
||||
|
||||
QT__Text {
|
||||
// text: "Right now, theProperty is: " + theProperty
|
||||
Component.onCompleted: text = "At creation time, theProperty is: " + theProperty
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
##### <MD_Title titleVar={5}> Manual bindings </MD_Title>
|
||||
|
||||
Sometimes (not often) you need to create a binding inside of a function, signal, or expression.
|
||||
If you need to change or attach a binding at runtime, the `Qt.binding` function can be used to
|
||||
create one.
|
||||
|
||||
The `Qt.binding` function takes another function as an argument, and when assigned to a property,
|
||||
the property will use that function as its binding expression.
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
QT__Text {
|
||||
id: boundText
|
||||
text: "not bound to anything"
|
||||
}
|
||||
|
||||
QT_qtquick11controls_Button {
|
||||
text: "bind the above text"
|
||||
onClicked: {
|
||||
if (boundText.text == "not bound to anything") {
|
||||
text = "press me";
|
||||
boundText.text = Qt.binding(() => `button is pressed: ${this.pressed}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, `boundText`'s `text` property is bound to the button's pressed state
|
||||
when the button is first clicked. When you press or unpress the button the text will
|
||||
be updated.
|
||||
|
||||
##### <MD_Title titleVar={5}> Removing bindings </MD_Title>
|
||||
|
||||
To remove a binding, just assign a new value to the property without using `Qt.binding`.
|
||||
|
||||
```qml
|
||||
QT__Item {
|
||||
QT__Text {
|
||||
id: boundText
|
||||
text: `button is pressed: ${theButton.pressed}`
|
||||
}
|
||||
|
||||
QT_qtquick11controls_Button {
|
||||
id: theButton
|
||||
text: "break the binding"
|
||||
onClicked: boundText.text = `button was pressed at the time the binding was broken: ${pressed}`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When the button is first pressed, the text will be updated, but once `onClicked` fires
|
||||
the text will be unbound, and even though it contains a reference to the `pressed` property,
|
||||
it will not be updated further by the binding.
|
||||
|
||||
### <MD_Title titleVar={3}> Lazy loading </MD_Title>
|
||||
|
||||
Often not all of your interface needs to load immediately. By default the QML
|
||||
engine initializes every object in the scene before showing anything onscreen.
|
||||
For parts of the interface you don't need to be immediately visible, load them
|
||||
asynchronously using a [LazyLoader](/docs/types/quickshell/lazyloader).
|
||||
See its documentation for more information.
|
||||
|
||||
#### <MD_Title titleVar={4}> Components </MD_Title>
|
||||
|
||||
Another delayed loading mechanism is the [Component](https://doc.qt.io/qt-6/qml-qtqml-component.html) type.
|
||||
This type can be used to create multiple instances of objects or lazily load them. It's used by types such
|
||||
as [Repeater](https://doc.qt.io/qt-6/qml-qtquick-repeater.html)
|
||||
and [Quickshell.Variants](/docs/types/quickshell/variants)
|
||||
to create instances of a component at runtime.
|
10
src/pages/docs/index.astro
Normal file
10
src/pages/docs/index.astro
Normal file
|
@ -0,0 +1,10 @@
|
|||
---
|
||||
import DocsLayout from "@layouts/DocsLayout.astro";
|
||||
---
|
||||
<DocsLayout title="Quickshell Docs" description="Quickshell Documentation">
|
||||
<h2>Docs</h2>
|
||||
<div class="root-nav">
|
||||
<h3><a href="/docs/configuration">Configuration</a></h3>
|
||||
<h3><a href="/docs/types">Type Definitions</a></h3>
|
||||
</div>
|
||||
</DocsLayout>
|
127
src/pages/docs/types/[type]/[name].astro
Normal file
127
src/pages/docs/types/[type]/[name].astro
Normal file
|
@ -0,0 +1,127 @@
|
|||
---
|
||||
import {
|
||||
parseMarkdown,
|
||||
getQMLTypeLink,
|
||||
} from "@config/io/helpers";
|
||||
import { generateTypeData } from "@config/io/generateTypeData";
|
||||
import { Flag } from "@icons";
|
||||
import DocsLayout from "@layouts/DocsLayout.astro";
|
||||
import TOC from "@components/navigation/sidebars/TOC.astro";
|
||||
import Properties from "@components/type/Properties.astro";
|
||||
import Functions from "@components/type/Functions.astro";
|
||||
import Signals from "@components/type/Signals.astro";
|
||||
import Variants from "@components/type/Variants.astro";
|
||||
import TransformMDCodeblocks from "@components/hooks/TransformMDCodeblocks.astro";
|
||||
import TransformLinks from "@components/hooks/TransformLinks.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const routes = await generateTypeData();
|
||||
|
||||
return routes
|
||||
.filter(route => route.name !== "index")
|
||||
.map(route => ({
|
||||
params: { type: route.type, name: route.name },
|
||||
props: { route },
|
||||
}));
|
||||
}
|
||||
|
||||
const { route } = Astro.props;
|
||||
|
||||
const data = route.data;
|
||||
|
||||
const sidebarFunctions =
|
||||
data.functions?.map(item => item.name) || null;
|
||||
|
||||
const propsKeys = data.properties
|
||||
? Object.keys(data.properties)
|
||||
: null;
|
||||
const signalKeys = data.signals
|
||||
? Object.keys(data.signals)
|
||||
: null;
|
||||
const variantKeys = data.variants
|
||||
? Object.keys(data.variants)
|
||||
: null;
|
||||
|
||||
const sidebarData = {
|
||||
properties: propsKeys,
|
||||
functions: sidebarFunctions,
|
||||
signals: signalKeys,
|
||||
variants: variantKeys,
|
||||
};
|
||||
|
||||
const superLink = data.super ? getQMLTypeLink(data.super) : null;
|
||||
|
||||
const details = parseMarkdown(data.details, route.name);
|
||||
---
|
||||
<TransformLinks/>
|
||||
<TransformMDCodeblocks/>
|
||||
|
||||
<DocsLayout title={`${route.name} - ${route.type}`} description={data?.description ?? ""}>
|
||||
<div class="docs">
|
||||
<div class="typedocs-content">
|
||||
<hr />
|
||||
<h2 class="typedocs-title">
|
||||
{route.name}:
|
||||
{data.super ? (
|
||||
<a
|
||||
target="_blank"
|
||||
href={superLink!}
|
||||
>
|
||||
{data.super.name}
|
||||
</a>
|
||||
):(
|
||||
<span class="type-datatype">{data.type}</span>
|
||||
)
|
||||
}
|
||||
</h2>
|
||||
{
|
||||
route && data ? (
|
||||
<section class="typedocs-data typedata">
|
||||
<subheading class="typedocs-subheading">
|
||||
<code class="type-module">import {data.module}</code>
|
||||
{data.flags ? (
|
||||
<div class="type-flags">{data.flags.map(flag => (
|
||||
<span class="type-flag">
|
||||
<Flag client:idle/>
|
||||
{flag}
|
||||
</span>
|
||||
))}</div>
|
||||
):null}
|
||||
<span id="injectedMd" set:html={details}/>
|
||||
{!details ? (<span class="toparse">{data.description}</span>):null}
|
||||
</subheading>
|
||||
{ data.properties && propsKeys ? (
|
||||
<Properties
|
||||
propsData={data.properties}
|
||||
propsKeys={propsKeys!}
|
||||
title={route.name}
|
||||
/>
|
||||
): null}
|
||||
{ data.functions && data.functions.length > 0 ? (
|
||||
<Functions
|
||||
funcData={data.functions}
|
||||
title={route.name}
|
||||
/>
|
||||
): null}
|
||||
{ data.signals && signalKeys ? (
|
||||
<Signals
|
||||
signalsData={data.signals}
|
||||
signalKeys={signalKeys}
|
||||
title={route.name}
|
||||
/>
|
||||
):null}
|
||||
{ data.variants && variantKeys ? (
|
||||
<Variants
|
||||
variantsData={data.variants}
|
||||
variantKeys={variantKeys}
|
||||
title={route.name}
|
||||
/>
|
||||
):null}
|
||||
</section>
|
||||
) : null
|
||||
}
|
||||
</div>
|
||||
<TOC mobile={false} types={sidebarData}/>
|
||||
</div>
|
||||
</DocsLayout>
|
||||
|
27
src/pages/docs/types/[type]/index.astro
Normal file
27
src/pages/docs/types/[type]/index.astro
Normal file
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
import DocsLayout from "@layouts/DocsLayout.astro";
|
||||
import { generateTypeData } from "@config/io/generateTypeData";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const routes = await generateTypeData();
|
||||
|
||||
return routes.map(route => ({
|
||||
params: { type: route.type, name: route.type },
|
||||
props: { route },
|
||||
}));
|
||||
}
|
||||
const { route } = Astro.props;
|
||||
---
|
||||
|
||||
<DocsLayout
|
||||
title={route.type + " Type Documentation"}
|
||||
description="Quickshell Type Documentation"
|
||||
>
|
||||
<hr />
|
||||
<h2>{route.type[0].toUpperCase() + route.type.slice(1)} Definitions</h2>
|
||||
<div class="root-nav">
|
||||
{route.data.contains!.map((item:string) => {
|
||||
return (<div><a class="root-nav-entry" href={`/docs/types/${route.data.module === "index"? route.data.name : route.data.module}/${item}`}>{item}</a></div>)
|
||||
})}
|
||||
</div>
|
||||
</DocsLayout>
|
8
src/pages/docs/types/index.astro
Normal file
8
src/pages/docs/types/index.astro
Normal file
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
import DocsLayout from "@layouts/DocsLayout.astro";
|
||||
---
|
||||
<DocsLayout title="Quickshell Type Definitions" description="Quickshell Type Documentation">
|
||||
<h2>Type Definitions</h2>
|
||||
<div class="root-nav">
|
||||
</div>
|
||||
</DocsLayout>
|
13
src/pages/index.astro
Normal file
13
src/pages/index.astro
Normal file
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
import BaseLayout from "@layouts/BaseLayout.astro";
|
||||
---
|
||||
<BaseLayout title="Quickshell" description="A fully user customizable desktop shell" image="/quickshell.png">
|
||||
<h2>A fully user customizable desktop shell</h2>
|
||||
<h3>Based on QtQuick</h3>
|
||||
<div class="root-nav">
|
||||
<h3><a href="/docs/configuration">Configuration</a></h3>
|
||||
<h3><a href="/docs/types">Type Definitions</a></h3>
|
||||
<h3><a href="https://git.outfoxxed.me/outfoxxed/quickshell-examples">Examples</a></h3>
|
||||
<h3><a href="https://git.outfoxxed.me/outfoxxed/quickshell">Source</a></h3>
|
||||
</div>
|
||||
</BaseLayout>
|
107
src/styles/css-config/animations.css
Normal file
107
src/styles/css-config/animations.css
Normal file
|
@ -0,0 +1,107 @@
|
|||
@property --percent {
|
||||
syntax: '<percentage>';
|
||||
initial-value: 0%;
|
||||
inherits: false;
|
||||
}
|
||||
|
||||
@keyframes pulseGreen {
|
||||
0% {
|
||||
background-color: hsl(var(--blue) 85 35 / 0.1);
|
||||
color: hsl(var(--blue) 100 69 / 1.0);
|
||||
}
|
||||
|
||||
50% {
|
||||
background-color: hsl(var(--green) 85 35 / 0.5);
|
||||
color: hsl(var(--green) 100 69 / 1.0);
|
||||
}
|
||||
|
||||
100% {
|
||||
background-color: hsl(var(--blue) 85 35 / 0.1);
|
||||
color: hsl(var(--blue) 100 69 / 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideDown {
|
||||
from {
|
||||
opacity: 0.001;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
height: var(--height);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
opacity: 1;
|
||||
height: var(--height);
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateIn {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateOut {
|
||||
from {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateIn90 {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes rotateOut90 {
|
||||
from {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@keyframes percentToFifty {
|
||||
from {
|
||||
--percent: 0%;
|
||||
}
|
||||
|
||||
to {
|
||||
--percent: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes percentToZero {
|
||||
from {
|
||||
--percent: 50%;
|
||||
}
|
||||
|
||||
to {
|
||||
--percent: 0%;
|
||||
}
|
||||
}
|
79
src/styles/css-config/base.css
Normal file
79
src/styles/css-config/base.css
Normal file
|
@ -0,0 +1,79 @@
|
|||
html {
|
||||
font-family: 'Rubik', Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.272;
|
||||
font-weight: 400;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
|
||||
color-scheme: light dark;
|
||||
/* accent */
|
||||
--green: 141;
|
||||
--accent-400: var(--green) 100 67;
|
||||
--accent-500: var(--green) 100 57;
|
||||
--accent-600: var(--green) 98 50;
|
||||
|
||||
/* secondary */
|
||||
--blue: 224;
|
||||
--secondary-400: var(--blue) 100 68;
|
||||
--secondary-500: var(--blue) 100 58;
|
||||
--secondary-600: var(--blue) 53 41;
|
||||
--secondary-700: var(--blue) 43 31;
|
||||
--secondary-800: var(--blue) 23 21;
|
||||
--secondary-900: var(--blue) 44 7;
|
||||
|
||||
/* primary */
|
||||
--white: 194;
|
||||
--bg-400: var(--white) 33 100;
|
||||
--bg-500: var(--white) 12 96;
|
||||
--bg-600: var(--white) 12 76;
|
||||
--bg-700: var(--white) 12 56;
|
||||
--bg-800: var(--white) 12 36;
|
||||
--bg-900: var(--white) 12 16;
|
||||
}
|
||||
|
||||
html.dark {
|
||||
/* accent */
|
||||
--green: 141;
|
||||
--accent-400: var(--green) 100 67;
|
||||
--accent-500: var(--green) 95 55;
|
||||
--accent-600: var(--green) 90 40;
|
||||
|
||||
/* secondary */
|
||||
--white: 194;
|
||||
--secondary-400: var(--white) 33 100;
|
||||
--secondary-500: var(--white) 33 96;
|
||||
--secondary-600: var(--white) 33 76;
|
||||
--secondary-700: var(--white) 33 56;
|
||||
--secondary-800: var(--white) 35 36;
|
||||
--secondary-900: var(--white) 44 7;
|
||||
|
||||
/* primary */
|
||||
--blue: 224;
|
||||
--bg-400: var(--blue) 90 65;
|
||||
--bg-500: var(--blue) 83 45;
|
||||
--bg-700: var(--blue) 82 25;
|
||||
--bg-800: var(--blue) 82 15;
|
||||
--bg-900: var(--blue) 82 3;
|
||||
|
||||
/* docs */
|
||||
--prop-color: 350 78 70;
|
||||
--prop-link-color: 350 78 60;
|
||||
--func-color: 50 78 70;
|
||||
--func-link-color: 50 78 60;
|
||||
--signal-color: 270 78 70;
|
||||
--signal-link-color: 270 85 60;
|
||||
--var-color: 190 78 70;
|
||||
--var-link-color: 190 85 60;
|
||||
--inner-param-color: 215 60 70;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
42
src/styles/css-config/code.css
Normal file
42
src/styles/css-config/code.css
Normal file
|
@ -0,0 +1,42 @@
|
|||
:where(p, li):has(>code) code {
|
||||
padding-inline: 0.272rem;
|
||||
border-radius: 0.272rem;
|
||||
color: hsl(var(--blue) 100 69);
|
||||
background-color: hsl(var(--blue) 85 35 / 0.1);
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 1rem;
|
||||
margin: 0.618rem;
|
||||
border-radius: 0.618rem;
|
||||
overflow: auto;
|
||||
text-wrap: wrap;
|
||||
|
||||
&>button {
|
||||
all: unset;
|
||||
width: 3svh;
|
||||
height: 3svh;
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 0.5rem;
|
||||
font-size: 1.618rem;
|
||||
font-weight: 500;
|
||||
border-radius: 0.272rem;
|
||||
padding: 0.418rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: hsl(var(--blue) 100 69);
|
||||
background-color: hsl(var(--blue) 85 35 / 0.1);
|
||||
cursor: pointer;
|
||||
transition: color 0.25s;
|
||||
|
||||
&:hover {
|
||||
color: hsl(var(--blue) 100 75);
|
||||
}
|
||||
|
||||
&.copied {
|
||||
animation: pulseGreen 0.5s cubic-bezier(0, 1, 0.6, 1);
|
||||
}
|
||||
}
|
||||
}
|
31
src/styles/css-config/colors.css
Normal file
31
src/styles/css-config/colors.css
Normal file
|
@ -0,0 +1,31 @@
|
|||
.typeprop-link {
|
||||
color: hsl(var(--prop-link-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--prop-link-color)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.typesignal-link {
|
||||
color: hsl(var(--signal-link-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--signal-link-color)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.typefunc-link {
|
||||
color: hsl(var(--func-link-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--func-link-color)) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.typevar-link {
|
||||
color: hsl(var(--var-link-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--var-link-color)) !important;
|
||||
}
|
||||
}
|
41
src/styles/css-config/modal.css
Normal file
41
src/styles/css-config/modal.css
Normal file
|
@ -0,0 +1,41 @@
|
|||
.search-output {
|
||||
position: fixed;
|
||||
inset: 0 25svw;
|
||||
top: 3.6rem;
|
||||
z-index: 33;
|
||||
max-height: 100svw;
|
||||
max-width: 50svw;
|
||||
|
||||
overflow: scroll;
|
||||
overflow-x: show;
|
||||
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-auto-rows: max-content;
|
||||
row-gap: 0.618rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.search-output::before {
|
||||
content: "";
|
||||
position: fixed;
|
||||
inset: -4rem;
|
||||
backdrop-filter: blur(4px);
|
||||
overflow: hidden;
|
||||
background-color: hsla(0 0 0 / 0.3);
|
||||
}
|
||||
|
||||
.search-output_item {
|
||||
height: max-content;
|
||||
padding: 1rem;
|
||||
border: 2px solid rgba(200, 200, 200, 0.23);
|
||||
border-radius: 12px;
|
||||
background-color: hsl(0 0 0);
|
||||
overflow: hidden;
|
||||
|
||||
& mark {
|
||||
all: unset;
|
||||
background: hsla(53 800 34 / 0.5);
|
||||
padding-inline: 3px;
|
||||
}
|
||||
}
|
1
src/styles/css-config/search.css
Normal file
1
src/styles/css-config/search.css
Normal file
|
@ -0,0 +1 @@
|
|||
@import "./modal.css";
|
66
src/styles/docs/collapsible.css
Normal file
66
src/styles/docs/collapsible.css
Normal file
|
@ -0,0 +1,66 @@
|
|||
[data-scope='collapsible'][data-part='root'] {
|
||||
--height: max-content;
|
||||
padding: 0.673rem;
|
||||
background-color: hsl(var(--white) 40 50 / 0.1);
|
||||
border-radius: 0.618rem;
|
||||
}
|
||||
|
||||
[data-scope='collapsible'][data-part='content'] {
|
||||
transition: all 250ms;
|
||||
padding: 0;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
[data-scope='collapsible'][data-part='content'][data-state='open'] {
|
||||
animation: slideDown 250ms;
|
||||
display: flex;
|
||||
gap: 1.272rem;
|
||||
flex-direction: column;
|
||||
padding: 0.618em;
|
||||
margin-left: 22px;
|
||||
|
||||
& p {
|
||||
margin-block: 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[data-scope='collapsible'][data-part='content'][data-state='closed'] {
|
||||
animation: slideUp 200ms;
|
||||
padding: 0;
|
||||
margin-left: 0;
|
||||
|
||||
& svg {
|
||||
animation: rotateOut90 250ms;
|
||||
}
|
||||
}
|
||||
|
||||
[data-scope='collapsible'][data-part='trigger'] {
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
font-size: 1.117rem;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
gap: 0.272rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0.618rem;
|
||||
border-radius: 0.618rem;
|
||||
|
||||
&:hover {
|
||||
background-color: hsl(var(--white) 40 50 / 0.1);
|
||||
}
|
||||
|
||||
&[data-state='open'] {
|
||||
& svg {
|
||||
animation: rotateIn90 250ms forwards;
|
||||
}
|
||||
}
|
||||
|
||||
&[data-state='closed'] {
|
||||
& svg {
|
||||
animation: rotateOut90 250ms forwards;
|
||||
}
|
||||
}
|
||||
}
|
13
src/styles/docs/docs-config.css
Normal file
13
src/styles/docs/docs-config.css
Normal file
|
@ -0,0 +1,13 @@
|
|||
.small {
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.5s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
& p {
|
||||
font-size: 0.841rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
407
src/styles/docs/docs-types.css
Normal file
407
src/styles/docs/docs-types.css
Normal file
|
@ -0,0 +1,407 @@
|
|||
.root-nav {
|
||||
margin-left: 1em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
& .root-nav-entry {
|
||||
margin-block: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.types-nav {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.typedocs-title {
|
||||
& a {
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.5s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#injectedMd {
|
||||
&>p:not(:first-child) {
|
||||
margin-block: 0.724rem;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.type-module {
|
||||
color: hsl(var(--blue) 75 60);
|
||||
}
|
||||
|
||||
.typedocs-subheading {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.typedocs-content {
|
||||
|
||||
&>p {
|
||||
margin-block: 0.618rem;
|
||||
}
|
||||
|
||||
& hr {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.318rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.typedocs-data {
|
||||
& subheading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.618rem;
|
||||
}
|
||||
}
|
||||
|
||||
.typedata {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
|
||||
& .typedata-root {
|
||||
margin-bottom: 0.618rem;
|
||||
border-radius: 12px;
|
||||
padding: 1.272rem;
|
||||
transition: border 0.3s;
|
||||
|
||||
|
||||
&>.typedata-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.272rem;
|
||||
margin: 0;
|
||||
margin-top: 0.272rem;
|
||||
|
||||
&>svg {
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .typedata-details {
|
||||
margin-top: 1.618rem;
|
||||
}
|
||||
|
||||
& .typedata-params {
|
||||
& .typedata-param {
|
||||
margin-top: 0.272rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.117rem;
|
||||
|
||||
&>svg {
|
||||
height: 1.272rem;
|
||||
width: 1.272rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .typedata-detailsdata,
|
||||
.typedocs-subheading {
|
||||
&>p {
|
||||
margin-top: 0.618rem;
|
||||
}
|
||||
|
||||
& .typeprop-link {
|
||||
color: hsl(var(--prop-link-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--prop-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .typesignal-link {
|
||||
color: hsl(var(--signal-link-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--signal-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .typefunc-link {
|
||||
color: hsl(var(--func-link-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--func-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .typevar-link {
|
||||
color: hsl(var(--var-link-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--var-link-color))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.typedata-link {
|
||||
display: inline-flex;
|
||||
align-items: baseline;
|
||||
gap: 3px;
|
||||
|
||||
& svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.typedata-param {
|
||||
color: hsla(var(--prop-link-color) / 1);
|
||||
}
|
||||
|
||||
.type-datatype {
|
||||
color: #808080;
|
||||
opacity: 0.8;
|
||||
width: max-content;
|
||||
transition: opacity 0.5s;
|
||||
|
||||
&>a {
|
||||
opacity: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.type-flags {
|
||||
& .type-flag {
|
||||
margin: 0;
|
||||
margin-top: 0.272rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.117rem;
|
||||
color: hsl(var(--inner-param-color))
|
||||
}
|
||||
}
|
||||
|
||||
.typeprops {
|
||||
& .typeprop-root {
|
||||
border: 1px solid hsla(var(--prop-color) / 0.3);
|
||||
|
||||
&:hover {
|
||||
border: 1px solid hsla(var(--prop-color) / 0.6);
|
||||
|
||||
& .type-datatype {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
& .typeprop-name {
|
||||
color: hsl(var(--prop-link-color));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.typefuncs {
|
||||
& .typefunc-root {
|
||||
border: 1px solid hsla(var(--func-color) / 0.3);
|
||||
|
||||
&:hover {
|
||||
border: 1px solid hsla(var(--func-color) / 0.6);
|
||||
|
||||
& .type-datatype {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
& .typefunc-name {
|
||||
color: hsl(var(--func-link-color));
|
||||
}
|
||||
|
||||
& .typefunc-params {
|
||||
|
||||
& .typefunc-param {
|
||||
margin-top: 0.272rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.117rem;
|
||||
|
||||
&>svg {
|
||||
height: 1.272rem;
|
||||
width: 1.272rem;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.typesignals {
|
||||
& .typesignal-root {
|
||||
border: 1px solid hsla(var(--signal-color) / 0.3);
|
||||
|
||||
&:hover {
|
||||
border: 1px solid hsla(var(--signal-color) / 0.6);
|
||||
|
||||
& .typesignal-doclink {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
& .typesignal-name {
|
||||
position: relative;
|
||||
width: max-content;
|
||||
color: hsl(var(--signal-link-color));
|
||||
|
||||
& .typesignal-doclink {
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
right: -12px;
|
||||
opacity: 0.8;
|
||||
scale: 75%;
|
||||
}
|
||||
}
|
||||
|
||||
& .typesignal-params {
|
||||
& .typesignal-param {
|
||||
margin-top: 0.272rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.117rem;
|
||||
|
||||
&>svg {
|
||||
height: 1.272rem;
|
||||
width: 1.272rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.typevariants {
|
||||
& .typevariant-root {
|
||||
border: 1px solid hsla(var(--var-color) / 0.3);
|
||||
|
||||
&:hover {
|
||||
border: 1px solid hsla(var(--var-color) / 0.6);
|
||||
|
||||
& .typevariant-doclink {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
& .typevariant-name {
|
||||
position: relative;
|
||||
width: max-content;
|
||||
color: hsl(var(--var-link-color));
|
||||
|
||||
& .typevariant-doclink {
|
||||
position: absolute;
|
||||
top: -12px;
|
||||
right: -12px;
|
||||
opacity: 0.8;
|
||||
scale: 75%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.typedocs-content {
|
||||
margin-inline: 1.272rem;
|
||||
|
||||
& section {
|
||||
min-width: 30svw;
|
||||
}
|
||||
|
||||
&>p {
|
||||
margin-block: 1.217rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.typedata {
|
||||
max-width: 47svw;
|
||||
}
|
||||
|
||||
.typedata-detailsdata,
|
||||
.typedocs-subheading {
|
||||
max-width: 42svw;
|
||||
|
||||
}
|
||||
|
||||
.root-nav {
|
||||
min-width: 45svw;
|
||||
}
|
||||
|
||||
.types-nav {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 5rem;
|
||||
right: 10svw;
|
||||
width: 250px;
|
||||
max-height: 90svh;
|
||||
overflow-y: scroll;
|
||||
z-index: 10;
|
||||
|
||||
& .props-list {
|
||||
color: hsl(var(--prop-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--prop-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .funcs-list {
|
||||
color: hsl(var(--func-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--func-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .signals-list {
|
||||
color: hsl(var(--signal-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--signal-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .vars-list {
|
||||
color: hsl(var(--var-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--var-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .types-list {
|
||||
list-style: none;
|
||||
|
||||
& .types-item {
|
||||
margin-block: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.478rem;
|
||||
|
||||
& svg {
|
||||
opacity: 0.6;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
& svg {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
124
src/styles/docs/docs.css
Normal file
124
src/styles/docs/docs.css
Normal file
|
@ -0,0 +1,124 @@
|
|||
@import "./docs-config.css";
|
||||
@import "./docs-types.css";
|
||||
|
||||
.docslayout-root {
|
||||
margin: 0.618rem;
|
||||
margin-top: 3.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.docs,
|
||||
.docslayout-root {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.spacer-desktop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.c-breadcrumbs {
|
||||
margin-top: 1.056rem;
|
||||
margin-bottom: 0.318rem;
|
||||
max-width: 100svw;
|
||||
}
|
||||
|
||||
.docs-content {
|
||||
& section {
|
||||
max-width: 95svw;
|
||||
margin-block: 1.618rem;
|
||||
}
|
||||
|
||||
& p {
|
||||
margin-block: 0.618rem;
|
||||
}
|
||||
|
||||
& hr {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.318rem;
|
||||
}
|
||||
}
|
||||
|
||||
.heading {
|
||||
& > [id] {
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
& .heading-hashtag {
|
||||
& svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
opacity: 0.5;
|
||||
transition: opacity 0.5s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hr {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 2.478rem;
|
||||
}
|
||||
|
||||
.markdown-alert {
|
||||
margin-block: 0.618rem;
|
||||
|
||||
& > *:not(:first-child) {
|
||||
margin-block: 0.724rem;
|
||||
}
|
||||
}
|
||||
|
||||
.markdown-alert-title {
|
||||
text-transform: lowercase;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.docs,
|
||||
.docslayout-root {
|
||||
gap: 0.648rem;
|
||||
}
|
||||
|
||||
.docslayout-root {
|
||||
margin-left: calc(1.618rem + 260px);
|
||||
}
|
||||
|
||||
.docslayout-inner {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.docs-content {
|
||||
margin-inline: 1.272rem;
|
||||
|
||||
& section {
|
||||
margin-block: 1.884rem;
|
||||
}
|
||||
|
||||
& p {
|
||||
margin-block: 1.217rem;
|
||||
}
|
||||
}
|
||||
|
||||
.c-breadcrumbs {
|
||||
margin-inline: 1.272rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.docs-content {
|
||||
& section {
|
||||
max-width: 45svw;
|
||||
}
|
||||
}
|
||||
|
||||
.docslayout-root {
|
||||
margin-inline: calc(10svw + 260px);
|
||||
}
|
||||
}
|
171
src/styles/docs/nav/nav-tree.css
Normal file
171
src/styles/docs/nav/nav-tree.css
Normal file
|
@ -0,0 +1,171 @@
|
|||
[data-scope="accordion"][data-part="root"] {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15em;
|
||||
|
||||
& [data-part="item"] {
|
||||
padding: 6px;
|
||||
|
||||
& [data-part="item"] {
|
||||
padding-right: 0;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-scope="accordion"][data-part="item-trigger"] {
|
||||
background-color: hsl(var(--bg-900));
|
||||
position: relative;
|
||||
border: unset;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 0.15em;
|
||||
|
||||
& p:has(a) {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
padding-inline: 4px;
|
||||
border-radius: 20px;
|
||||
|
||||
&:hover {
|
||||
position: relative;
|
||||
width: max-content;
|
||||
padding: 4px;
|
||||
overflow: scroll;
|
||||
z-index: 101;
|
||||
}
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
z-index: -1;
|
||||
inset: -1px;
|
||||
background-color: hsla(var(--green) 80 70 / 0.3);
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
&::before {
|
||||
background-color: hsla(var(--green) 80 70 / 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-scope="accordion"][data-part="item-indicator"] {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
margin-left: 3px;
|
||||
|
||||
&:hover {
|
||||
background-color: hsl(var(--blue) 30 30);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[data-scope="accordion"][data-part="item-indicator"][data-state="open"] {
|
||||
animation: rotateIn 250ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
[data-scope="accordion"][data-part="item-indicator"][data-state="closed"] {
|
||||
animation: rotateOut 250ms ease-in-out;
|
||||
}
|
||||
|
||||
[data-scope="accordion"][data-part="item-content"] {
|
||||
--height: 709;
|
||||
margin-block: 0.175rem;
|
||||
|
||||
&>.arktree-item,
|
||||
[data-part="item"] {
|
||||
margin-left: 21px;
|
||||
margin-block: 0.117rem;
|
||||
}
|
||||
|
||||
& .arktree-item,
|
||||
[data-part="item-content"]>div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.15em;
|
||||
margin-left: 24px;
|
||||
margin-top: 0.224em;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(to right,
|
||||
hsla(var(--accent-400) / 0.5) var(--percent),
|
||||
hsla(var(--accent-400) / 0) 75%);
|
||||
animation: percentToZero 250ms ease-in-out forwards;
|
||||
width: 0;
|
||||
transition: width 0.25s;
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
width: 100%;
|
||||
animation: percentToFifty 250ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
|
||||
&>a {
|
||||
padding-top: 1em;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-scope="accordion"][data-part="item-content"][data-state="open"] {
|
||||
animation: slideDown 250ms ease-in-out;
|
||||
}
|
||||
|
||||
[data-scope="accordion"][data-part="item-content"][data-state="closed"] {
|
||||
animation: slideUp 200ms ease-in-out;
|
||||
}
|
||||
|
||||
.__current-type-doc {
|
||||
color: hsl(var(--blue) 100 70);
|
||||
|
||||
& [data-part="item-trigger"] a {
|
||||
color: hsl(var(--blue) 100 70) !important;
|
||||
|
||||
}
|
||||
|
||||
&>a {
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(to right,
|
||||
hsla(var(--accent-400) / 0.5) var(--percent),
|
||||
hsla(var(--accent-400) / 0) 100%);
|
||||
animation: percentToZero 250ms ease-in-out forwards;
|
||||
width: 100%;
|
||||
|
||||
}
|
||||
|
||||
&:hover::before {
|
||||
animation: percentToFifty 250ms ease-in-out forwards;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
127
src/styles/docs/nav/nav.css
Normal file
127
src/styles/docs/nav/nav.css
Normal file
|
@ -0,0 +1,127 @@
|
|||
@import "./nav-tree.css";
|
||||
|
||||
.nav-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-toggle {
|
||||
position: unset;
|
||||
height: 24px;
|
||||
font-size: 1.614rem;
|
||||
overflow-y: scroll;
|
||||
max-height: 500px;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& svg,
|
||||
div {
|
||||
height: max-content;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& .nav-items {
|
||||
position: absolute;
|
||||
z-index: 11;
|
||||
overflow-y: scroll;
|
||||
top: 2.5rem;
|
||||
left: -1rem;
|
||||
width: 0;
|
||||
height: 0;
|
||||
font-size: 0.745rem;
|
||||
font-weight: 600;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
transition:
|
||||
width 0.3s ease,
|
||||
height 0.3s ease,
|
||||
background-color 0.3s ease,
|
||||
backdrop-filter 0.3s ease,
|
||||
padding 0.3s ease;
|
||||
|
||||
&.shown {
|
||||
padding: 0.3rem;
|
||||
width: 100svw;
|
||||
height: 50svh;
|
||||
background-color: hsl(var(--bg-900) / 0.6);
|
||||
backdrop-filter: blur(3px) saturate(180%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
& .navtree {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
padding: 6px;
|
||||
|
||||
& [data-part="item"] {
|
||||
margin-left: unset;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
padding-right: 0;
|
||||
|
||||
& [data-part="item-content"] {
|
||||
& [data-part="item-trigger"] {
|
||||
width: 93%;
|
||||
margin-left: 1em;
|
||||
|
||||
& p {
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
& [data-part="item-content"] {
|
||||
width: 80%;
|
||||
margin-left: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
.nav-wrapper-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
display: block;
|
||||
width: 250px;
|
||||
position: fixed;
|
||||
top: 5rem;
|
||||
left: 1.618rem;
|
||||
flex-shrink: 0;
|
||||
overflow: scroll;
|
||||
max-height: 90svh;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.navtree {
|
||||
width: 100%;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.nav-wrapper {
|
||||
left: 10svw;
|
||||
}
|
||||
}
|
29
src/styles/docs/toc/intro-toc.css
Normal file
29
src/styles/docs/toc/intro-toc.css
Normal file
|
@ -0,0 +1,29 @@
|
|||
.toc-wrapper-mobile .toc-content {
|
||||
& .toc_a {
|
||||
transition: color 0.33s;
|
||||
color: hsl(var(--green) 72 40);
|
||||
}
|
||||
|
||||
& ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& li {
|
||||
list-style: none;
|
||||
|
||||
&.active {
|
||||
& > .toc_a {
|
||||
color: hsl(var(--green) 72 60);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .toc_heading {
|
||||
margin: 0;
|
||||
margin-block: 0.618rem;
|
||||
|
||||
& * {
|
||||
margin-left: 0.348rem;
|
||||
}
|
||||
}
|
||||
}
|
125
src/styles/docs/toc/toc.css
Normal file
125
src/styles/docs/toc/toc.css
Normal file
|
@ -0,0 +1,125 @@
|
|||
@import "./types-toc.css";
|
||||
@import "./intro-toc.css";
|
||||
|
||||
.toc-wrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toc-wrapper-mobile {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.toc-toggle {
|
||||
--width: 100svw;
|
||||
display: block;
|
||||
position: unset;
|
||||
height: 24px;
|
||||
font-size: 1.614rem;
|
||||
max-height: 500px;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& > svg {
|
||||
height: 100%;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
& div {
|
||||
width: 100%;
|
||||
height: max-content;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
& .toc-mobile {
|
||||
position: absolute;
|
||||
overflow-y: scroll;
|
||||
top: 2.6rem;
|
||||
right: -1rem;
|
||||
width: 0;
|
||||
height: 0;
|
||||
font-size: 0.745rem;
|
||||
font-weight: 600;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
transition:
|
||||
width 0.3s ease,
|
||||
height 0.3s ease,
|
||||
background-color 0.3s ease,
|
||||
backdrop-filter 0.3s ease,
|
||||
padding 0.3s ease;
|
||||
|
||||
&.shown {
|
||||
padding: 0.3rem;
|
||||
width: var(--width);
|
||||
height: 50svh;
|
||||
background-color: hsl(var(--bg-900) / 0.6);
|
||||
backdrop-filter: blur(3px) saturate(180%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1280px) {
|
||||
.toc-toggle {
|
||||
--width: 70svw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
.toc-wrapper-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toc-wrapper {
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 5rem;
|
||||
right: 10svw;
|
||||
width: 250px;
|
||||
max-height: 90svh;
|
||||
overflow-y: scroll;
|
||||
z-index: 10;
|
||||
|
||||
& .toc_a {
|
||||
transition: color 0.33s;
|
||||
color: hsl(var(--green) 72 40);
|
||||
}
|
||||
|
||||
& ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
& li {
|
||||
list-style: none;
|
||||
|
||||
&.active {
|
||||
& > .toc_a {
|
||||
color: hsl(var(--green) 72 60);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
& .toc_heading {
|
||||
margin: 0;
|
||||
margin-block: 0.618rem;
|
||||
|
||||
& * {
|
||||
margin-left: 0.348rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
63
src/styles/docs/toc/types-toc.css
Normal file
63
src/styles/docs/toc/types-toc.css
Normal file
|
@ -0,0 +1,63 @@
|
|||
.toc-content {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
padding: 6px;
|
||||
|
||||
& .props-list {
|
||||
color: hsl(var(--prop-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--prop-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .funcs-list {
|
||||
color: hsl(var(--func-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--func-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .signals-list {
|
||||
color: hsl(var(--signal-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--signal-link-color));
|
||||
}
|
||||
}
|
||||
|
||||
& .vars-list {
|
||||
color: hsl(var(--var-color));
|
||||
|
||||
& a {
|
||||
color: hsl(var(--var-link-color));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.types-list {
|
||||
list-style: none;
|
||||
z-index: 21;
|
||||
|
||||
& .types-item {
|
||||
margin-block: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.478rem;
|
||||
|
||||
& svg {
|
||||
opacity: 0.6;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
& svg {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
142
src/styles/global.css
Normal file
142
src/styles/global.css
Normal file
|
@ -0,0 +1,142 @@
|
|||
@import "remark-github-blockquote-alert/alert.css";
|
||||
@import "./css-config/base.css";
|
||||
@import "./css-config/animations.css";
|
||||
@import "./css-config/code.css";
|
||||
@import "./css-config/search.css";
|
||||
@import "./css-config/colors.css";
|
||||
@import "./docs/nav/nav.css";
|
||||
@import "./docs/toc/toc.css";
|
||||
@import "./docs/docs.css";
|
||||
@import "./docs/collapsible.css";
|
||||
|
||||
/* color styling */
|
||||
.header {
|
||||
background-color: hsl(var(--blue) 100 88);
|
||||
box-shadow: 0 3px 3px 3px hsla(var(--white) 100 0 / 0.3);
|
||||
}
|
||||
|
||||
.baselayout,
|
||||
.docslayout {
|
||||
background-color: hsl(var(--bg-500));
|
||||
color: hsl(var(--secondary-900));
|
||||
}
|
||||
|
||||
a {
|
||||
color: hsla(var(--accent-600));
|
||||
text-decoration: none;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
html.dark {
|
||||
& .header {
|
||||
background-color: hsl(var(--secondary-900));
|
||||
color: hsl(var(--secondary-500));
|
||||
}
|
||||
|
||||
& .baselayout,
|
||||
.docslayout {
|
||||
background-color: hsl(var(--bg-900));
|
||||
color: hsl(var(--secondary-400));
|
||||
}
|
||||
|
||||
& a {
|
||||
color: hsl(var(--accent-500));
|
||||
}
|
||||
}
|
||||
|
||||
/* layout and positioning */
|
||||
.unset {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
.search {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 3.5rem;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
display: none;
|
||||
font-size: 1.614em;
|
||||
font-weight: 700;
|
||||
line-height: 1.272;
|
||||
}
|
||||
|
||||
.header-title.mobile {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.header-item {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
justify-content: flex-start;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
justify-content: flex-end;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
|
||||
.spacer-mobile {
|
||||
font-size: 1.374rem;
|
||||
color: hsla(var(--white) 40 50 / 0.3);
|
||||
}
|
||||
|
||||
.theme-toggle {
|
||||
height: 24px;
|
||||
font-size: 1.614rem;
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
html {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.header-title.mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.menu-toggle,
|
||||
.spacer-mobile {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.spacer-desktop {
|
||||
display: block;
|
||||
font-size: 1.374rem;
|
||||
color: hsla(var(--white) 40 50 / 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {}
|
37
tsconfig.json
Normal file
37
tsconfig.json
Normal file
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"compilerOptions": {
|
||||
"plugins": [
|
||||
{
|
||||
"name": "@astrojs/ts-plugin"
|
||||
}
|
||||
],
|
||||
"jsx": "preserve",
|
||||
"jsxImportSource": "solid-js",
|
||||
"verbatimModuleSyntax": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@*": [
|
||||
"./*"
|
||||
],
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"@config/*": [
|
||||
"./src/config/*"
|
||||
],
|
||||
"@icons": [
|
||||
"./src/components/icons.tsx"
|
||||
],
|
||||
"@components/*": [
|
||||
"./src/components/*"
|
||||
],
|
||||
"@layouts/*": [
|
||||
"./src/layouts/*"
|
||||
],
|
||||
"@styles/*": [
|
||||
"./src/styles/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue