Пересобранный и видоизмененный conky для ОСГОС

This commit is contained in:
commit 3a48cf8c86
738 changed files with 217718 additions and 0 deletions

16
web/.eslintrc.json Normal file
View file

@ -0,0 +1,16 @@
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"next/core-web-vitals",
"prettier"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"root": true,
"settings": {
"mdx/code-blocks": true,
"mdx/language-mapper": {},
"sort-imports": "error"
}
}

47
web/.gitignore vendored Normal file
View file

@ -0,0 +1,47 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
*.tsbuildinfo
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local
# vercel
.vercel
# netlify
.netlify
# IDEs
.vscode/
# Cypress videos
cypress/videos
# Fuse.js index (we generate this on the fly)
public/static/fuse-index.json

View file

@ -0,0 +1,24 @@
interface ArrowIconProps {
className: string
}
export default function ArrowIcon({ className }: ArrowIconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
className={className}
>
<path
className="stroke-current text-primary"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M5 12h14M12 19l7-7-7-7"
></path>
</svg>
)
}

View file

@ -0,0 +1,36 @@
interface CopyleftIconProps {
className?: string
width?: number
height?: number
}
export default function CopyleftIcon({
className,
width,
height,
}: CopyleftIconProps) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
width={width || 980}
height={height || 980}
fill="none"
viewBox="0 0 980 980"
className={className}
>
<circle
cx="490"
cy="490"
r="440"
fill="none"
strokeWidth="100"
className="stroke-current"
/>
<path
d="M219,428H350a150,150 0 1 1 0,125H219a275,275 0 1 0 0-125z"
strokeWidth="1"
className="stroke-current fill-current"
/>
</svg>
)
}

82
web/components/Docs.tsx Normal file
View file

@ -0,0 +1,82 @@
import { Documentation } from '../utils/doc-utils'
import { Link as LinkIcon } from 'react-feather'
import Link from 'next/link'
import { useRouter } from 'next/router'
export interface DocsProps {
docs: Documentation
braces: boolean
assign: boolean
}
export default function Docs({ docs, braces, assign }: DocsProps) {
const router = useRouter()
return (
<div className="prose dark:prose-invert prose-lg w-full max-w-3xl">
<div className="px-2 lg:px-4" dangerouslySetInnerHTML={{ __html: docs.desc_md }} />
<div className="divide-y divide-gray-700/25 dark:divide-gray-300/25">
{docs.values.map((doc) => {
const target = router.asPath.endsWith(`#${doc.name}`)
return (
<div
id={doc.name}
key={doc.name}
className={
'pt-4 scroll-mt-16 overflow-auto ' +
(target
? 'bg-rose-300 dark:bg-rose-900'
: 'bg-slate-200 dark:bg-slate-800 target:bg-rose-300 target:dark:bg-rose-900') +
' hover:bg-opacity-25 dark:hover:bg-opacity-25 hover:ring-1 ring-black dark:ring-white ring-inset'
}
>
<div className="flex">
<div className="px-2 lg:pl-4 py-3">
<Link href={`#${doc.name}`} data-anchor-name={doc.name}>
<LinkIcon size={20} />
</Link>
</div>
<div className="flex-col p-1 pr-2 lg:pr-4">
<div>
{braces && <code>$&#123;</code>}
<code className="text-lg p-1 mx-1 bg-fuchsia-200 dark:bg-fuchsia-900 font-bold">
{doc.name}
</code>
{typeof doc.args != 'undefined' && doc.args.length > 0 && (
<>
{assign && <code>=</code>}
<em>
{doc.args.map((arg) => (
<code
className="text-lg p-1 mx-1 bg-slate-200 dark:bg-slate-800"
key={arg}
>
{arg}
</code>
))}
</em>
</>
)}
{braces && <code>&#125;</code>}
</div>
<div
className="py-2"
dangerouslySetInnerHTML={{ __html: doc.desc_md }}
/>
{typeof doc.default != 'undefined' && (
<div>
Default:{' '}
<code className="px-1 mx-1 bg-slate-200 dark:bg-slate-800">
{doc.default}
</code>
</div>
)}
</div>
</div>
</div>
)
})}
</div>
</div>
)
}

87
web/components/Doggy.tsx Normal file
View file

@ -0,0 +1,87 @@
import * as React from 'react'
const Doggy: React.FunctionComponent = () => {
return (
// from https://undraw.co/
<svg className="w-full" width="656" height="447.5061" viewBox="0 0 656 447.5061">
<path
d="M108.40148,315.94602c-3.16093,.3401-6.60921,.85055-10.27718,1.59744-15.55246,3.16687-15.87526,13.72736-15.73896,16.14847l-.10566-.06586c-1.56382,2.50859-2.54325,5.0473-3.13519,7.44337l-.71221-32.36652c.46052-.9602,.87954-2.04896,1.22889-3.16494,1.16419,3.58504,3.07256,6.97195,4.7176,6.74748,2.13909-.2919,3.07167-6.58596,2.67265-11.05881,2.72016,3.50314,7.2981,7.24972,9.02511,6.09446,1.38001-.92313,.56173-4.72359-.94492-8.17871,3.13929,1.89132,6.61613,3.13439,7.69503,1.93342,1.47227-1.63885-2.05557-7.23082-5.30905-10.35235,4.35496,.40613,9.89372-.28771,10.25684-2.26977,.2992-1.6331-2.99722-3.69384-6.5255-5.02014,3.5439-.93441,6.85789-2.56256,6.74798-4.17324-.13057-1.91338-5.05068-3.19543-9.29526-3.39051,3.20154-2.50471,6.14321-5.96711,5.24362-7.53303-.82704-1.43962-4.67439-.88198-8.22406,.38596,2.10064-3.00326,3.5775-6.38742,2.45277-7.54556-1.40106-1.44266-6.37359,1.04738-9.76711,3.79978,.66062-4.28797,.32665-9.70052-1.59659-10.19022-1.60895-.40967-3.88929,2.73886-5.4527,6.16866-.69097-3.59931-2.08979-7.01645-3.7042-7.01645-2.29982,0-4.16418,6.93399-4.16418,11.45149,0,.25536,.00677,.49574,.01843,.72541-.23083-.26761-.49538-.54719-.79707-.84018-3.24076-3.14728-9.51391-6.64064-11.11616-4.99082-1.12474,1.15814,.35212,4.5423,2.45277,7.54556-3.54967-1.26794-7.39702-1.82559-8.22406-.38596-1.11127,1.93439,3.63883,6.76323,7.4999,9.10499-.2321-.02796-.47595-.0515-.73547-.0692-4.50701-.30752-11.55183,1.08049-11.7084,3.37497-.10991,1.61067,3.20409,3.23883,6.74798,4.17324-3.52829,1.3263-6.8247,3.38704-6.5255,5.02014,.36312,1.98206,5.90188,2.6759,10.25684,2.26977-3.25348,3.12154-6.78132,8.7135-5.30905,10.35235,1.0789,1.20097,4.55573-.0421,7.69503-1.93342-1.50665,3.45512-2.32492,7.25558-.94492,8.17871,1.82373,1.21996,6.82541-3.02601,9.4625-6.67949-.2687,4.46965,.80077,10.61004,2.9228,10.85236,.88742,.10134,1.82777-.84507,2.67643-2.3023l-2.40356,34.79012c-.56184,4.05159,3.03199,6.69542,3.35318,8.36434l.51842,23.5593c-.58508-1.67794-1.37316-3.3952-2.43548-5.09933l-.10566,.0659c.13628-2.42136-.18675-12.98155-15.73896-16.14836-3.66797-.74689-7.11625-1.25734-10.27718-1.59744-3.73116-.40145-5.1866,4.70611-1.81595,6.35581,4.88144,2.38917,9.4733,5.39935,11.86668,8.96403,6.11901,9.11359,14.5737,3.84542,15.77578,3.0275,1.34266,2.23509,2.22306,4.48687,2.77913,6.62945l.27531,12.51144c-.06744,.2959-.11414,.46159-.11777,.47403l.12902,.03782,.66628,30.27884,6.18057-.04965-2.21068-30.94947c.37149-1.66957,1.51435-7.35722,0-13.26406l-2.21068-24.31744h0c-.0055-.01877,.12548,.63842,0,0l-2.8837-9.55504c.41933-2.95604,1.3925-6.28098,3.36894-9.57097,1.20206,.81791,9.65677,6.08611,15.77579-3.0275,2.39338-3.56469,6.98524-6.57486,11.86668-8.96403,3.37065-1.64971,1.91521-6.75726-1.81595-6.35581Zm-28.23342,71.6385l-.11651-5.2952c.23499,2.00214,.2231,3.80933,.11651,5.2952Z"
fill="#f2f2f2"
/>
<path
d="M581.40148,315.94602c-3.16093,.3401-6.60921,.85055-10.27718,1.59744-15.55246,3.16687-15.87526,13.72736-15.73896,16.14847l-.10566-.06586c-1.56382,2.50859-2.54325,5.0473-3.13519,7.44337l-.71221-32.36652c.46052-.9602,.87954-2.04896,1.22889-3.16494,1.16419,3.58504,3.07256,6.97195,4.7176,6.74748,2.13909-.2919,3.07167-6.58596,2.67265-11.05881,2.72016,3.50314,7.2981,7.24972,9.02511,6.09446,1.38001-.92313,.56173-4.72359-.94492-8.17871,3.13929,1.89132,6.61613,3.13439,7.69503,1.93342,1.47227-1.63885-2.05557-7.23082-5.30905-10.35235,4.35496,.40613,9.89372-.28771,10.25684-2.26977,.2992-1.6331-2.99722-3.69384-6.5255-5.02014,3.5439-.93441,6.85789-2.56256,6.74798-4.17324-.13057-1.91338-5.05068-3.19543-9.29526-3.39051,3.20154-2.50471,6.14321-5.96711,5.24362-7.53303-.82704-1.43962-4.67439-.88198-8.22406,.38596,2.10064-3.00326,3.5775-6.38742,2.45277-7.54556-1.40106-1.44266-6.37359,1.04738-9.76711,3.79978,.66062-4.28797,.32665-9.70052-1.59659-10.19022-1.60895-.40967-3.88929,2.73886-5.4527,6.16866-.69097-3.59931-2.08979-7.01645-3.7042-7.01645-2.29982,0-4.16418,6.93399-4.16418,11.45149,0,.25536,.00677,.49574,.01843,.72541-.23083-.26761-.49538-.54719-.79707-.84018-3.24076-3.14728-9.51391-6.64064-11.11616-4.99082-1.12474,1.15814,.35212,4.5423,2.45277,7.54556-3.54967-1.26794-7.39702-1.82559-8.22406-.38596-1.11127,1.93439,3.63883,6.76323,7.4999,9.10499-.2321-.02796-.47595-.0515-.73547-.0692-4.50701-.30752-11.55183,1.08049-11.7084,3.37497-.10991,1.61067,3.20409,3.23883,6.74798,4.17324-3.52829,1.3263-6.8247,3.38704-6.5255,5.02014,.36312,1.98206,5.90188,2.6759,10.25684,2.26977-3.25348,3.12154-6.78132,8.7135-5.30905,10.35235,1.0789,1.20097,4.55573-.0421,7.69503-1.93342-1.50665,3.45512-2.32492,7.25558-.94492,8.17871,1.82373,1.21996,6.82541-3.02601,9.4625-6.67949-.2687,4.46965,.80077,10.61004,2.9228,10.85236,.88742,.10134,1.82777-.84507,2.67643-2.3023l-2.40356,34.79012c-.56184,4.05159,3.03199,6.69542,3.35318,8.36434l.51842,23.5593c-.58508-1.67794-1.37316-3.3952-2.43548-5.09933l-.10566,.0659c.13628-2.42136-.18675-12.98155-15.73896-16.14836-3.66797-.74689-7.11625-1.25734-10.27718-1.59744-3.73116-.40145-5.1866,4.70611-1.81595,6.35581,4.88144,2.38917,9.4733,5.39935,11.86668,8.96403,6.11901,9.11359,14.5737,3.84542,15.77578,3.0275,1.34266,2.23509,2.22306,4.48687,2.77913,6.62945l.27531,12.51144c-.06744,.2959-.11414,.46159-.11777,.47403l.12902,.03782,.66628,30.27884,6.18057-.04965-2.21068-30.94947c.37149-1.66957,1.51435-7.35722,0-13.26406l-2.21068-24.31744h0c-.0055-.01877,.12548,.63842,0,0l-2.8837-9.55504c.41933-2.95604,1.3925-6.28098,3.36894-9.57097,1.20206,.81791,9.65677,6.08611,15.77579-3.0275,2.39338-3.56469,6.98524-6.57486,11.86668-8.96403,3.37065-1.64971,1.91521-6.75726-1.81595-6.35581Zm-28.23342,71.6385l-.11651-5.2952c.23499,2.00214,.2231,3.80933,.11651,5.2952Z"
fill="#f2f2f2"
/>
<path
d="M485.44292,216.85712l-2.89819-69.09314s-3.25537-41.01923-5.85986-55.34344c-2.60437-14.32416-20.18408-10.4176-20.18408-10.4176l1.95337,41.01929,8.42773,41.97778,6.55957,57.16211c-.33789,1.6535-.5293,3.45087-.5293,5.33789,0,8.00812,3.34424,14.5,7.46973,14.5s7.46973-6.49188,7.46973-14.5c0-4.21252-.93103-7.99371-2.40869-10.64288Z"
fill="#ffb6b6"
/>
<path
d="M344.36666,198.95449c3.08175-3.6707,6.77264-6.1651,10.08696-7.12522l38.98077-44.06526s3.2555-41.01927,5.8599-55.34346c2.6044-14.32419,20.18408-10.41759,20.18408-10.41759l-1.9533,41.01927-11.06869,44.27477-45.61337,38.72532c-.79655,1.45114-1.77304,2.90165-2.94266,4.29478-5.54929,6.60955-13.07747,9.42409-16.81477,6.28638-3.73715-3.13771-2.2682-11.03944,3.28109-17.64899Z"
fill="#ffb6b6"
/>
<polygon
points="430.13131 76.46862 434.65312 59.08516 452.2687 58.23783 457.53901 82.20035 430.13131 76.46862"
fill="#ffb6b6"
/>
<polygon
points="430.13131 76.46862 434.65312 59.08516 452.2687 58.23783 457.53901 82.20035 430.13131 76.46862"
opacity=".1"
/>
<g>
<polygon
points="385.1488 361.25683 368.64994 360.3143 370.81424 410.94912 381.99418 411.58853 385.1488 361.25683"
fill="#ffb6b6"
/>
<path
d="M369.38449,394.93938l5.72891,2.61332-.00743,3.47275s1.38213-4.01923,8.80109-4.89615l.88559,3.36705s2.87736,3.30264,2.38384,9.27645l-1.8885,15.99763-5.59502-.12882-.90369-11.132s-1.5413-2.52606-8.64068,9.47718c0,0-7.82052,1.9711-19.93118-2.08958-1.66453-.55811-2.82078-2.11212-2.79901-3.86759,.01202-.96878,.39069-1.8641,1.60549-2.18526,3.0922-.8175,6.11176,.21911,8.10866-2.24898,1.9969-2.46809,12.25193-17.656,12.25193-17.656Z"
fill="#2f2e41"
/>
</g>
<g>
<polygon
points="473.69834 361.25683 457.19947 360.3143 459.36377 410.94912 470.54371 411.58853 473.69834 361.25683"
fill="#ffb6b6"
/>
<path
d="M457.93402,394.93938l5.72891,2.61332-.00743,3.47275s1.38213-4.01923,8.80109-4.89615l.88559,3.36705s2.87736,3.30264,2.38384,9.27645l-1.8885,15.99763-5.59502-.12882-.90369-11.132s-1.5413-2.52606-8.64068,9.47718c0,0-7.82052,1.9711-19.93118-2.08958-1.66453-.55811-2.82078-2.11212-2.79901-3.86759,.01202-.96878,.39069-1.8641,1.60549-2.18526,3.0922-.8175,6.11176,.21911,8.10866-2.24898,1.9969-2.46809,12.25193-17.656,12.25193-17.656Z"
fill="#2f2e41"
/>
</g>
<circle cx="443.67515" cy="49.78935" r="19.53299" fill="#ffb6b6" />
<path
d="M454.75196,53.03639s-8.58797-14.05193-17.1846-12.50095c-8.59662,1.55098-13.07809,2.95128-13.07809,2.95128,0,0-3.50075-11.20367,8.25239-16.57884,0,0-16.63273-13.92589,1.09406-22.86307,17.72679-8.93718,28.7577-.27561,28.7577-.27561,0,0,14.67194,12.44289-1.59513,23.95832,0,0,14.07934,16.03785-.14106,31.35764,0,0,2.5104-11.66516-.9046-9.27374-3.41501,2.39142-5.20067,3.22497-5.20067,3.22497Z"
fill="#2f2e41"
/>
<path
d="M456.82639,77.77082l-26.69508-3.2555-20.50963,16.60304s-10.20026,28.4207-2.6044,45.57697c.18201,.4111,.32555,28.97393,.32555,28.97393h56.71743s-.05495-19.44142,2.11224-26.48271c.3096-1.0059,.66455-1.75873,1.07149-2.16567,3.2555-3.2555,11.06869-24.09068,7.16209-33.20607-3.9066-9.11539-17.57969-26.04398-17.57969-26.04398Z"
fill="#6c63ff"
/>
<polygon
points="407.01728 164.36705 371.20681 278.30947 363.39361 368.1612 385.20545 368.1612 403.76178 284.82046 429.31616 236.57758 454.54754 374.67219 476.56445 374.67219 476.03383 187.80663 464.01412 164.36705 407.01728 164.36705"
fill="#2f2e41"
/>
<rect y="422.15664" width="656" height="2" fill="#3f3d56" />
<g>
<path
d="M336.26362,187.42757c-6.50229-10.47037-21.67223-12.93381-32.93992-7.93906-11.26769,4.99475-19.0565,15.64429-24.35105,26.77426-5.29455,11.12996-8.62502,23.12151-14.06681,34.18023-16.01157,32.53845-48.43603,53.78965-68.83256,83.77469-15.32504,22.52941-23.44045,49.88437-22.88108,77.12621,.21426,10.43456,1.9016,21.41209,8.41343,29.56819,9.66535,12.1059,26.77774,14.58924,42.18653,16.18335,8.95057,.92597,19.93647,.89956,24.54601-6.82846,1.82337-3.05693-17.77064-13.72146-17.45627-17.26697,1.69638-19.13259,23.34373-31.28271,25.04011-50.41531,2.6597,13.33132-.7489,27.0942-.2109,40.67759,.53799,13.58339,7.23345,28.94778,20.53418,31.75641,4.10444,.86671,8.73862,.26717,11.84946-2.54713,3.11084-2.81429,4.01941-8.13071,1.24812-11.27992,2.83767,2.89821,7.62638,3.6514,11.21672,1.76422,3.59034-1.88718,5.68538-6.25866,4.90727-10.23943-.5722-2.92733-2.46138-5.38952-4.0973-7.88359-8.86167-13.51019-11.17027-31.11518-6.09158-46.45342,8.97777-27.11391,38.45973-44.81167,43.45404-72.93321,3.70897-20.88416-6.8726-44.54836,4.35624-62.54329,5.06032-8.10949,13.65271-13.22163,20.50586-19.88533,6.85315-6.6637,12.21238-16.85889,8.29043-25.57606l-35.02913-.03819"
fill="#3f3d56"
/>
<path
d="M228.23833,426.7864c-.87183,0-1.76514-.23291-2.62842-.6958-1.98853-1.06641-3.48804-3.1748-3.82056-5.37207-.55322-3.6543,1.1875-7.20312,2.58618-10.05469,3.82593-7.79883,7.60791-16.13818,8.16528-24.80273,.60889-9.46045-3.22852-18.22949-9.77563-22.34082-4.86572-3.05469-11.36255-3.84033-18.29248-2.21045-6.13794,1.44336-11.6521,4.39404-17.71045,7.81689-.4812,.27197-1.09082,.10254-1.36255-.37891-.27173-.48047-.10205-1.09082,.37866-1.3623,5.96631-3.37109,11.85034-6.521,18.23633-8.02295,7.45483-1.75195,14.49146-.87793,19.81396,2.46436,7.16016,4.49609,11.36328,13.98047,10.70824,24.1626-.58104,9.03027-4.45141,17.57617-8.36596,25.55518-1.34229,2.73682-2.86377,5.83887-2.40405,8.87451,.23779,1.57129,1.35815,3.14209,2.78833,3.90869,.69653,.37402,1.75879,.71045,2.86206,.19727,.50146-.23438,1.0957-.0166,1.32886,.48389s.0166,1.0957-.48413,1.32861c-.64282,.2998-1.32617,.44873-2.02368,.44873Z"
fill="#2f2e41"
/>
<path
d="M304.87324,186.74131c-1.30944,3.58632,.43745,7.60574,2.85869,10.55768,2.42124,2.95194,5.531,5.3258,7.66451,8.49194,4.47207,6.63654,3.80132,15.70946,.36506,22.93685-3.43625,7.22739-9.22913,13.01799-14.91052,18.65402,.41995-2.41674-.80214-4.85632-2.46745-6.65736s-3.7617-3.13792-5.62402-4.73441c-8.67234-7.43441-11.3852-20.98221-6.24412-31.18266"
fill="#2f2e41"
/>
<path
d="M186.42241,400.10863c-7.34594-.9001-13.55119,5.70986-20.88233,6.72341-8.47755,1.17204-16.27381-5.53591-20.04567-13.21807-3.77186-7.68216-4.73983-16.40208-7.33471-24.55739-2.59488-8.15531-7.60116-16.41523-15.75739-19.00722-3.42187-1.08745-7.29353-1.02838-10.47242,.64087-3.17889,1.66926-5.50871,5.07712-5.4292,8.66675,.09784,4.41706,3.4851,7.95244,5.53718,11.8651,3.26521,6.2257,3.16378,13.59894,4.60607,20.47941,3.46765,16.54261,16.92397,30.57084,33.30801,34.72366,8.47267,2.14754,17.95749,1.6559,25.29692-3.09078s11.81291-14.30671,9.09603-22.61434"
fill="#3f3d56"
/>
</g>
</svg>
)
}
export default Doggy

34
web/components/Footer.tsx Normal file
View file

@ -0,0 +1,34 @@
import getConfig from 'next/config'
import CopyleftIcon from './CopyleftIcon'
import Link from 'next/link'
const Footer: React.FunctionComponent = () => {
const { publicRuntimeConfig } = getConfig()
const { modifiedDate, modifiedYear, gitHash } = publicRuntimeConfig
return (
<div className="max-w-3xl mx-auto flex py-4 items-center">
<div className="px-2 lg:px-4">
<CopyleftIcon width={20} height={20} />
</div>
<div className="pl-1 pr-2 lg:pr-4 font-sans text-xs">
<p>
{modifiedYear} Conky developers, updated{' '}
{new Date(modifiedDate).toLocaleString()} UTC
{gitHash && (
<>
{' '}
<Link
target="_blank"
href={`https://github.com/brndnmtthws/conky/commit/${gitHash}`}
>
{`(${gitHash})`}
</Link>
</>
)}
</p>
</div>
</div>
)
}
export default Footer

19
web/components/GitHub.tsx Normal file
View file

@ -0,0 +1,19 @@
export default function GitHub() {
return (
<div className="flex rounded-3xl p-1">
<svg
height="32"
width="32"
aria-hidden="true"
viewBox="0 0 16 16"
data-view-component="true"
className="fill-current stroke-0"
>
<path
fillRule="evenodd"
d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
></path>
</svg>
</div>
);
}

74
web/components/Header.tsx Normal file
View file

@ -0,0 +1,74 @@
import Link from 'next/link'
import { useRouter } from 'next/router'
import GitHub from './GitHub'
import ThemeSwitcher from './ThemeSwitcher'
import * as React from 'react'
import Search from './Search'
type HeaderProps = {
name: string
setDarkMode: (state: boolean) => void
}
interface NavLinkProps {
href: string
name: string
}
const NavLink: React.FunctionComponent<NavLinkProps> = (props) => {
const router = useRouter()
const bg = router.asPath.startsWith(props.href)
? 'bg-rose-100 dark:bg-rose-900'
: ''
return (
<Link
href={props.href}
className={`m-0.5 p-1 self-end hover:ring-1 ring-black dark:ring-white hover:bg-rose-300 dark:hover:bg-rose-700 ${bg} rounded`}
>
{props.name}
</Link>
)
}
export default function Header({ name, setDarkMode }: HeaderProps) {
const router = useRouter()
return (
<div className="border-b-1 backdrop-blur-lg bg-white dark:bg-black bg-opacity-20 dark:bg-opacity-20 transition">
<header className="max-w-3xl mx-auto m-0 py-1 px-2 lg:px-4 grow flex w-full">
<h1 className="text-3xl dark:text-white self-end mr-1">
<Link href="/" className="font-bold" data-cy="top-link">
{name}
</Link>
</h1>
{router.asPath != '/' && (
<div className="hidden sm:flex text-md items-stretch self-stretch mr-1">
<NavLink href="/variables" name="Vars" />
<NavLink href="/config_settings" name="Config" />
<NavLink href="/lua" name="Lua" />
</div>
)}
<div className="flex-grow" />
<Search />
<div className="flex">
<div className="flex items-center border-r mx-1 px-1 border-slate-700">
<a href="https://github.com/brndnmtthws/conky">
<GitHub />
</a>
</div>
<div className="ml-1 pl-1 flex place-content-center place-items-center">
<ThemeSwitcher setDarkMode={setDarkMode} />
</div>
</div>
</header>
{router.asPath != '/' && (
<div className="flex sm:hidden text-md items-stretch self-stretch px-1 pb-1">
<NavLink href="/variables" name="Vars" />
<NavLink href="/config_settings" name="Config" />
<NavLink href="/lua" name="Lua" />
</div>
)}
</div>
)
}

61
web/components/Layout.tsx Normal file
View file

@ -0,0 +1,61 @@
import { useEffect, useState } from 'react'
import Footer from './Footer'
import Header from './Header'
function darkModeDefault() {
if (typeof window === 'undefined') {
return false
} else {
const theme = localStorage.getItem('theme')
return (
theme === 'dark' ||
(!theme && window.matchMedia('(prefers-color-scheme: dark)').matches)
)
}
}
interface LayoutProps {
children: React.ReactNode
}
export default function Layout({ children }: LayoutProps) {
const [darkMode, setDarkMode] = useState(darkModeDefault())
useEffect(() => {
if (darkMode) {
document.documentElement.classList.add('dark')
localStorage.setItem('theme', 'dark')
} else {
document.documentElement.classList.remove('dark')
localStorage.setItem('theme', 'light')
}
}, [darkMode])
useEffect(() => {
const darkQuery = window.matchMedia('(prefers-color-scheme: dark)')
darkQuery.onchange = (e) => {
if (e.matches) {
setDarkMode(true)
} else {
setDarkMode(false)
}
}
}, [])
return (
<div>
<div className="sticky top-0 z-10">
<Header name="Conky" setDarkMode={setDarkMode} />
</div>
<div className="relative pb-4 md:pt-4">
<div className="flex flex-col items-center max-w-3xl w-full mx-auto">
{children}
</div>
</div>
<div className="relative">
<Footer />
</div>
</div>
)
}

16
web/components/SEO.tsx Normal file
View file

@ -0,0 +1,16 @@
import Head from 'next/head'
interface SEOProps {
title: string
description: string
}
export default function SEO({ title, description }: SEOProps) {
return (
<Head>
<title>{title}</title>
<meta name="description" content={description} />
<meta property="og:title" content={title} />
</Head>
)
}

213
web/components/Search.tsx Normal file
View file

@ -0,0 +1,213 @@
import Fuse, { FuseResult } from 'fuse.js'
import React, { Fragment, useCallback, useEffect, useState } from 'react'
import { Search as SearchIcon } from 'react-feather'
import { SearchIndex, SearchItem } from '../utils/search'
import { Dialog, Transition, Combobox } from '@headlessui/react'
import { useRouter } from 'next/router'
export interface SearchProps {}
interface SearchResultProps {
result: FuseResult<SearchItem>
selected: boolean
active: boolean
}
const SearchResult: React.FunctionComponent<SearchResultProps> = (props) => {
const excerpt = (s: string) => {
if (s.length < 120) {
return <>{s}</>
} else {
return <>{s.substring(0, 120)}&hellip;</>
}
}
const bg_for = (s: string) => {
const bg = 'p-1 rounded bg-opacity-20 '
if (s === 'var') {
return bg + 'bg-blue-500'
}
if (s === 'config') {
return bg + 'bg-green-500'
}
if (s === 'lua') {
return bg + 'bg-red-500'
}
return bg
}
const selection = props.active ? 'bg-slate-300 dark:bg-slate-700' : ''
return (
<div className={`m-1 rounded flex flex-col p-2 ${selection}`}>
<hr className="border-black/10 dark:border-white/10 mb-2"/>
<div className="flex justify-between">
<div>
<code className="text-lg p-1 mx-1 bg-fuchsia-200 dark:bg-fuchsia-900 font-bold">
{props.result.item.name}
</code>
</div>
<div className={bg_for(props.result.item.kind)}>
{props.result.item.kind}
</div>
</div>
<div>
<p className="w-11/12">{excerpt(props.result.item.desc)}</p>
</div>
</div>
)
}
const Search: React.FunctionComponent<SearchProps> = () => {
const router = useRouter()
const [searchText, setSearchText] = useState('')
const [searchResults, setSearchResults] = useState<FuseResult<SearchItem>[]>(
[]
)
const [fuse, setFuse] = React.useState<Fuse<SearchItem> | undefined>(
undefined
)
const fusePromise = async () => {
const data = await fetch('/static/fuse-index.json')
console.log(data)
const searchIndex: SearchIndex = await data.json()
console.log(searchIndex)
return new Fuse(searchIndex.list, {}, Fuse.parseIndex(searchIndex.index))
}
React.useEffect(() => {
fusePromise().then((fuse) => setFuse(fuse))
}, [])
const [isOpen, setIsOpen] = useState(false)
const handleKeyPress = useCallback(
(event: KeyboardEvent) => {
if (event.key === 'k' && (event.metaKey || event.ctrlKey) && isOpen) {
setIsOpen(false)
event.preventDefault()
} else if (
(event.key == '/' ||
(event.key === 'k' && (event.metaKey || event.ctrlKey))) &&
!isOpen
) {
setIsOpen(true)
event.preventDefault()
}
},
[isOpen]
)
useEffect(() => {
document.addEventListener('keydown', handleKeyPress)
return () => {
document.removeEventListener('keydown', handleKeyPress)
}
}, [handleKeyPress])
if (!fuse) {
return (
<div className="flex items-center ml-2">
<SearchIcon size={32} />
</div>
)
}
const setSearch = (value: string) => {
setSearchText(value)
const searchResult = fuse.search(value)
setSearchResults(searchResult)
}
const onChange = (value?: FuseResult<SearchItem>) => {
if (value) {
if (value.item.kind === 'var') {
router.push(`/variables#${value.item.name}`, undefined, {
scroll: false,
})
}
if (value.item.kind === 'config') {
router.push(`/config_settings#${value.item.name}`, undefined, {
scroll: false,
})
}
if (value.item.kind === 'lua') {
router.push(`/lua#${value.item.name}`, undefined, { scroll: false })
}
setIsOpen(false)
}
}
const closeModal = () => {
setIsOpen(false)
}
const openModal = () => {
setIsOpen(true)
}
return (
<>
<div className="flex items-center ml-2">
<button onClick={openModal} title="Search (/ or ⌘K)">
<SearchIcon size={32} />
</button>
</div>
<Transition appear show={isOpen} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={closeModal}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
>
<div className="fixed inset-0 bg-black bg-opacity-25 backdrop-blur-sm" />
</Transition.Child>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 scale-95"
enterTo="opacity-100 scale-100"
>
<div className="fixed inset-0">
<div className="flex h-screen w-screen items-start justify-center p-16 text-center">
<Dialog.Panel className="flex flex-col max-h-full w-full max-w-2xl p-1 bg-gray-200 dark:bg-gray-800 transform rounded-xl text-left align-middle shadow transition-all border border-gray-800 dark:border-white border-opacity-10 dark:border-opacity-10">
<Combobox nullable onChange={onChange}>
<div className="flex">
<Combobox.Label className="flex items-center ml-2">
<SearchIcon size={32} />
</Combobox.Label>
<Combobox.Input
placeholder="Search docs (/ or ⌘K)"
className="mx-1 p-2 w-full bg-gray-200 dark:bg-gray-800 outline-none"
onChange={(e) => setSearch(e.target.value)}
/>
</div>
<Combobox.Options className="flex flex-col h-full overflow-auto">
{searchResults.length === 0 && searchText !== '' ? (
<div className="relative cursor-default select-none py-2 px-4 text-gray-500">
No results.
</div>
) : (
searchResults.map((r) => (
<Combobox.Option key={r.refIndex} value={r}>
{({ selected, active }) => (
<SearchResult
result={r}
selected={selected}
active={active}
/>
)}
</Combobox.Option>
))
)}
</Combobox.Options>
</Combobox>
</Dialog.Panel>
</div>
</div>
</Transition.Child>
</Dialog>
</Transition>
</>
)
}
export default Search

View file

@ -0,0 +1,81 @@
const sunIcon = (
<svg
xmlns="http://www.w3.org/2000/svg"
width="25"
height="24"
fill="none"
viewBox="0 0 25 24"
className="dark:opacity-50"
>
<g
stroke="#fff"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
clipPath="url(#clip0_192_823)"
>
<path d="M12.5 17a5 5 0 100-10 5 5 0 000 10zM12.5 1v2M12.5 21v2M4.72 4.22l1.42 1.42M18.86 18.36l1.42 1.42M1.5 12h2M21.5 12h2M4.72 19.78l1.42-1.42M18.86 5.64l1.42-1.42"></path>
</g>
<defs>
<clipPath id="clip0_192_823">
<path
className="fill-current text-white"
d="M0 0H24V24H0z"
transform="translate(.5)"
></path>
</clipPath>
</defs>
</svg>
)
const moonIcon = (
<svg
xmlns="http://www.w3.org/2000/svg"
width="25"
height="24"
fill="none"
viewBox="0 0 21 20"
>
<path
stroke="#fff"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
className="stroke-current text-gray-400 dark:text-white"
d="M19.5 10.79A9 9 0 119.71 1a7 7 0 009.79 9.79v0z"
></path>
</svg>
)
type Props = {
setDarkMode: (state: boolean) => void
}
export default function ThemeSwitcher({ setDarkMode }: Props) {
return (
<div className="flex bg-gray-200 dark:bg-gray-700 rounded-2xl">
<button
type="button"
aria-label="Use Dark Mode"
title="Use Dark Mode"
onClick={() => {
setDarkMode(true)
}}
className="flex items-center dark:bg-purple-500 rounded-3xl justify-center align-center p-1 w-10 h-full transition"
>
{moonIcon}
</button>
<button
type="button"
aria-label="Use Light Mode"
title="Use Light Mode"
onClick={() => {
setDarkMode(false)
}}
className="flex items-center bg-purple-500 dark:bg-transparent rounded-3xl justify-center align-center p-1 w-10 h-full transition"
>
{sunIcon}
</button>
</div>
)
}

10
web/cypress.config.js Normal file
View file

@ -0,0 +1,10 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const { defineConfig } = require('cypress')
module.exports = defineConfig({
scrollBehavior: 'center',
e2e: {
baseUrl: 'http://localhost:3000',
supportFile: false,
},
})

View file

@ -0,0 +1,7 @@
{
"extends": ["eslint:recommended", "plugin:cypress/recommended", "prettier"],
"plugins": ["cypress"],
"settings": {
"sort-imports": "error"
}
}

View file

@ -0,0 +1,43 @@
describe('check index renders', () => {
beforeEach(() => {
cy.visit('/')
})
it('displays the title text', () => {
cy.get('[data-cy="top-link"]').contains(/^Conky$/)
})
})
describe('check config settings', () => {
beforeEach(() => {
cy.visit('/config_settings')
})
it('displays the title text', () => {
cy.get('[data-cy="top-link"]').contains(/^Conky$/)
cy.get('[data-cy="page-heading"]').contains(/^Configuration settings$/)
})
})
describe('check variables', () => {
beforeEach(() => {
cy.visit('/variables')
})
it('displays the title text', () => {
cy.get('[data-cy="top-link"]').contains(/^Conky$/)
cy.get('[data-cy="page-heading"]').contains(/^Variables$/)
})
it('has anchor links and can focus on them', () => {
cy.get('[data-anchor-name="acpiacadapter"]').click()
cy.get('[data-anchor-name="acpiacadapter"]').and('be.visible')
})
})
describe('check lua', () => {
beforeEach(() => {
cy.visit('/lua')
})
it('displays the title text', () => {
cy.get('[data-cy="top-link"]').contains(/^Conky$/)
cy.get('[data-cy="page-heading"]').contains(/^Lua API$/)
})
})

20
web/documents/about.mdx Normal file
View file

@ -0,0 +1,20 @@
---
title: About
description: What's Conky all about?
indexWeight: -10
---
Conky is a system monitor for X. Conky can display just about anything, either
on your root desktop or in its own window. Not only does Conky have many
built-in objects, it can also display just about any piece of information by
using scripts and other external programs.
Conky has more than 250 built in objects, including support for a plethora of OS
stats (uname, uptime, CPU usage, mem usage, disk usage, `top` like process
stats, and network monitoring, just to name a few), built in IMAP and POP3
support, built in support for many popular music players (MPD, XMMS2,
Audacious), and much much more. Conky can display this info either as text, or
using simple progress bars and graph widgets, with different fonts and colours.
If you'd like to get involved, please visit the project on GitHub at
https://github.com/brndnmtthws/conky.

View file

@ -0,0 +1,41 @@
---
title: Compiling
description: How to compile Conky from source
indexWeight: 0
---
Using CMake to build Conky is pretty easy, and here is how I do it:
1. From the top level source dir, create a build working dir, and cd into it
```shell-session
mkdir build
cd build
```
2. Run the cmake configuration process
```shell-session
cmake ../ # pass the path to the sources to cmake
```
OR
```shell-session
ccmake ../ # you can also use the fance curses interface, or try cmake-gui
```
3. Compile as usual, and enjoy the out-of-source goodness
```shell-session
make
sudo make install # if you want
```
There are a number of build options for Conky, and the best way to discover
them is to use the `ccmake` (or `cmake-gui`) CMake tool for browsing them.
Certain Conky build time features (such as doc generation) require third-party
applications, which you should be notified of via CMake. In the case of doc
generation, you'll need the docbook2X package (available on most
distributions).

View file

@ -0,0 +1,42 @@
---
title: Contributing
description: How to contribute to Conky
indexWeight: 0
---
Contributions are welcome from anyone.
## Pull Request Guidelines
When submitting PRs, please:
- Describe the changes, why they were necessary, etc
- Describe how the changes affect existing behaviour
- Describe how you tested and validated your changes
- Include unit tests when appropriate
- Include any relevant screenshots/evidence demonstrating that the changes work and have been tested
- Any new source files should include a GPLv3 license header
- All new code must be GPLv3 licensed
- Try to leave the code better than you found it
- PRs with failed checks may be ignored or closed; please make sure
the build and checks pass if possible (and notify someone when the build
system is not working)
_Patches submitted in issues, email, or elsewhere may be ignored._
## Coding Style
Code should be formatted using `clang-format`. By configuring Conky with `cmake -DCHECK_CODE_QUALITY=ON`, you will be able to run `make clang-format` to automatically format code.
## Git hooks
To make life easier, you can use
[lefthook](https://github.com/evilmartians/lefthook) to handle some basic
linting with a pre-commit hook, as defined in [lefthook.yml](lefthook.yml).
Follow the [installation guide for
lefhook](https://github.com/evilmartians/lefthook/blob/master/docs/install.md),
then run `lefthook install` to enable the hooks.
## Unit Testing
Conky uses the [Catch2](https://github.com/catchorg/Catch2) unit testing framework. If you are adding new functions or methods, please consider adding unit tests for that code. Additionally, if you'd like to add tests for existing code, that would be a welcome contribution.

83
web/documents/docker.mdx Normal file
View file

@ -0,0 +1,83 @@
---
title: Docker
description: A guide for running Conky with Docker
indexWeight: 0
---
## Getting the image
Build the image with:
```shell-session
docker build --tag=conky .
```
or if you want the commandline version:
```shell-session
docker build --build-arg X11=no --tag=conkycmd .
```
## Running the container
After building you can run the graphical version with:
```shell-session
docker run --rm -ti --net=host -e DISPLAY -v ~/.Xauthority:/root/.Xauthority conky
```
The commandline version is easier:
```shell-session
docker run --rm -ti conkycmd
```
If you want to pass some options to conky you can just add them if you mention
'conky' twice. Once for the image and once for the command. For example:
```shell-session
docker run --rm -ti --net=host -e DISPLAY -v ~/.Xauthority:/root/.Xauthority conky conky --version
```
will show you the version of conky. Since you don't need X for this you could also do:
```shell-session
docker run --rm -ti conkycmd conky --version
```
If you want to fiddle around a bit with the configuration first you could do:
```shell-session
docker run --rm -ti --net=host -e DISPLAY -v ~/.Xauthority:/root/.Xauthority conky bash
```
_change things is in the configuration_
```shell-session
conky -c configurationfile ; exit
```
See the docker docs to a lot of other ways to (ab)use this container
## Why?
Disadvantages of using the docker:
- You'll have to install docker.
This can be done by following the instructions in
https://docs.docker.com/install/ to install docker CE. But just using the
packagemanager of your distro like you would do with other software will work.
- A lot of info will be about the current container instead of the whole system
- At the moment our docker image is still in development fase.
Advantages of using docker:
- During the installation you won't have to care about which compilers,
libraries, ... are installed. Neither should you care about how to use them.
- The containerization of conky will make it a lot harder to let problems with
conky affect the rest of the system.
- Your Conky will run in exactly the same environment as everyone else's, so
if it works for someone it will work for everyone. No matter which distro you
are using or how you configured that distro. (The conky configuration itself
will matter and also the version of conky)
- Problems will be easier to recreate by developers causing faster debugging.

View file

@ -0,0 +1,37 @@
---
title: Syntax Highlighting
description: Enabling conkyrc syntax highlighting
indexWeight: 0
---
## Gedit
Note: this highlights based on syntax and does **NOT** attempt to validate arguments or keywords. The syntax highlighting is unlikely to be 100% accurate and is open to improvement.
The syntax highlighting will automatically be applied to all files with `conky` in their name. eg. `my_config.conky` (unfortunately it also triggers for the `conky.lang` file itself, you should set it to XML manually)
- [`gtksourceview` Syntax Highlight documentation][1]
- [Regex Tutorial](http://www.rexegg.com/)
- [Regex Testing](https://regex101.com/)
Developers: The main context (`id="conkyrc"`) is where gedit begins. This main context then references other sub-contexts. Each context can apply styles to itself, sub-strings from its regexs, or its contents (in the case of `<start><end>` "container" contexts). If you are ever confused by something, try searching for XML attributes in the [`gtksourceview` docs][1]. If you find a particularly complex regex, try using the Regex Tester linked above, and bear in mind that `gtksourceview` adds some extra regex syntax (i.e. `\%[ ... ]` and `\%{ ... }`).
---
for medit v1.1.1:
` /usr/share/medit/language-specs/conky.lang`
for medit - older versions:
`/usr/share/medit-1/language-specs/conky.lang`
for gedit v2.x
` /usr/share/gtksourceview-2.0/language-specs/conky.lang`
or (for single user)
` ~/.local/share/gtksourceview-2.0/language-specs/conky.lang`
for gedit v3.x
` /usr/share/gtksourceview-3.0/language-specs/conky.lang`
or (for single user)
` ~/.local/share/gtksourceview-3.0/language-specs/conky.lang`
[1]: https://developer.gnome.org/gtksourceview/stable/lang-reference.html

13
web/global.d.ts vendored Normal file
View file

@ -0,0 +1,13 @@
declare module 'next/config' {
type ConfigTypes = () => {
publicRuntimeConfig: {
modifiedDate: string
modifiedYear: string
gitHash: string?
}
}
declare const getConfig: ConfigTypes
export default getConfig
}

28
web/netlify.toml Normal file
View file

@ -0,0 +1,28 @@
[build]
command = "npm run build"
publish = ".next"
# Include the doc dir in the build ignore command
ignore = "git diff --quiet $CACHED_COMMIT_REF $COMMIT_REF . ../doc/"
[build.environment]
# cache Cypress binary in local "node_modules" folder
# so Netlify caches it
CYPRESS_CACHE_FOLDER = "./node_modules/CypressBinary"
# set TERM variable for terminal output
TERM = "xterm"
[[plugins]]
package = "@netlify/plugin-nextjs"
[[plugins]]
package = "netlify-plugin-cypress"
# do not test the deployed URL
[plugins.inputs]
enable = false
# test the local site
[plugins.inputs.preBuild]
enable = true
start = 'npx netlify dev'
wait-on = 'http://localhost:8888'

5
web/next-env.d.ts vendored Normal file
View file

@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

28
web/next.config.mjs Normal file
View file

@ -0,0 +1,28 @@
// @ts-check
import { spawn } from 'child_process'
const config = async (_phase, { _defaultConfig }) => {
const gitHash = await new Promise((resolve) => {
const git = spawn('git', ['rev-parse', '--short', 'HEAD'])
git.stdout.on('data', (data) => {
resolve(data.toString().trim())
})
git.on('error', () => {
resolve(undefined)
})
})
/**
* @type {import('next').NextConfig}
*/
const nextConfig = {
publicRuntimeConfig: {
modifiedDate: new Date().toISOString(),
modifiedYear: new Date().getFullYear(),
gitHash,
},
}
return nextConfig
}
export default config

30990
web/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

73
web/package.json Normal file
View file

@ -0,0 +1,73 @@
{
"name": "conky-website",
"description": "Conky website",
"author": "Conky developers",
"license": "GPL-3.0-only",
"contributors": [
"Brenden Matthews"
],
"homepage": "https://github.com/brndnmtthws/conky",
"bugs": {
"url": "https://github.com/brndnmtthws/conky/issues"
},
"scripts": {
"dev": "npm run write-search-index && next dev",
"dev:watch": "npm run write-search-index && next-remote-watch ./documents",
"build": "npm run write-search-index && next build",
"start": "npm run write-search-index && next start",
"export": "npm run write-search-index && next build && next export",
"lint": "next lint",
"e2e": "start-server-and-test dev http://localhost:3000 \"cypress open --e2e\"",
"e2e:headless": "start-server-and-test dev http://localhost:3000 \"cypress run --e2e\"",
"write-search-index": "tsx ./scripts/write-search-index.ts"
},
"dependencies": {
"@fontsource-variable/fira-code": "^5.0.17",
"@fontsource-variable/inter": "^5.0.17",
"@fontsource-variable/newsreader": "^5.0.18",
"@headlessui/react": "^1.7.18",
"@netlify/plugin-nextjs": "^5.0.0",
"@tailwindcss/typography": "^0.5.12",
"classnames": "^2.5.1",
"colord": "^2.9.3",
"follow-redirects": "^1.15.6",
"fuse.js": "^7.0.0",
"gray-matter": "^4.0.3",
"inter-ui": "^4.0.2",
"next": "^14.1.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-feather": "^2.0.10",
"rehype-parse": "^9.0.0",
"rehype-react": "^8.0.0"
},
"devDependencies": {
"@types/eslint": "^8.56.8",
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.12.7",
"@types/react": "^18.2.76",
"@types/react-dom": "^18.2.25",
"@typescript-eslint/eslint-plugin": "^7.6.0",
"@typescript-eslint/parser": "^7.6.0",
"autoprefixer": "^10.4.19",
"cypress": "^13.7.2",
"eslint": "^8.57.0",
"eslint-config-next": "^14.1.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-cypress": "^2.15.1",
"eslint-plugin-mdx": "^3.1.5",
"js-yaml": "^4.1.0",
"netlify-cli": "^17.22.1",
"netlify-plugin-cypress": "^2.2.1",
"next-remote-watch": "2.0.0",
"postcss": "^8.4.38",
"rehype-stringify": "^10.0.0",
"remark-gfm": "^4.0.0",
"remark-parse": "^11.0.0",
"remark-rehype": "^11.1.0",
"start-server-and-test": "^2.0.3",
"tailwindcss": "^3.4.3",
"tsx": "^4.7.2",
"typescript": "^5.4.5"
}
}

22
web/pages/404.tsx Normal file
View file

@ -0,0 +1,22 @@
import Layout from '../components/Layout'
import SEO from '../components/SEO'
import Doggy from '../components/Doggy'
export default function FourOhFour() {
return (
<Layout>
<SEO title="Conky" description="Conky documentation" />
<main className="w-full">
<div className="flex flex-col items-center">
<h1 className="text-4xl py-8 px-2 lg:px-4 font-extrabold text-center">404: Not found 😢</h1>
<Doggy />
<h2 className="text-3xl py-4 font-serif mb-8">Here&rsquo;s a doggy 😀</h2>
</div>
</main>
</Layout>
)
}
export function getStaticProps() {
return { props: {} }
}

11
web/pages/_app.tsx Normal file
View file

@ -0,0 +1,11 @@
import '@fontsource-variable/fira-code'
import '@fontsource-variable/inter'
import '@fontsource-variable/newsreader'
import '../styles/globals.css'
import { AppProps } from 'next/app'
function App({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
export default App

20
web/pages/_document.tsx Normal file
View file

@ -0,0 +1,20 @@
import React from 'react'
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
render() {
return (
<Html lang="en" className="theme-compiled scroll-smooth">
<Head />
<body
className={`antialiased bg-white dark:bg-gray-900 dark:text-white leading-base`}
>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument

View file

@ -0,0 +1,37 @@
import Layout from '../components/Layout'
import SEO from '../components/SEO'
import {
getConfigSettings,
Documentation,
filterDesc,
} from '../utils/doc-utils'
import Docs from '../components/Docs'
export interface ConfigSettingsProps {
config_settings: Documentation
}
export default function ConfigSettings(props: ConfigSettingsProps) {
return (
<Layout>
<SEO
title="Conky – Config settings"
description="Conky configuration settings"
/>
<main className="w-full pt-4">
<div>
<h1 className="text-2xl px-2 lg:px-4" data-cy="page-heading">
Configuration settings
</h1>
</div>
<Docs docs={props.config_settings} braces={false} assign={true} />
</main>
</Layout>
)
}
export async function getStaticProps() {
const config_settings = filterDesc(getConfigSettings())
return { props: { config_settings } }
}

View file

@ -0,0 +1,103 @@
import {
getNextDocumentBySlug,
getDocumentBySlug,
getPreviousDocumentBySlug,
documentFilePaths,
} from '../../utils/mdx-utils'
import Layout from '../../components/Layout'
import SEO from '../../components/SEO'
import { GetStaticProps } from 'next'
import Link from 'next/link'
import { unified } from 'unified'
import rehypeReact from 'rehype-react'
import * as prod from 'react/jsx-runtime'
import rehypeParse from 'rehype-parse'
import { createElement, Fragment, useEffect, useState } from 'react'
const production = { Fragment: prod.Fragment, jsx: prod.jsx, jsxs: prod.jsxs }
interface FrontMatter {
title: string
description: string
}
interface DocumentPageProps {
source: string
frontMatter: FrontMatter
}
export default function DocumentPage({
source,
frontMatter,
}: DocumentPageProps) {
const [children, setChildren] = useState(createElement(Fragment))
useEffect(
function () {
;(async function () {
const file = await unified()
.use(rehypeParse, { fragment: true })
// @ts-expect-error: the react types are missing.
.use(rehypeReact, { ...production, components: { a: Link } })
.process(source)
setChildren(file.result)
})()
},
[source]
)
return (
<Layout>
<SEO
title={`${frontMatter.title}`}
description={frontMatter.description}
/>
<article className="p-2 pt-4 lg:p-4 w-full">
<header>
<h1 className="text-3xl md:text-5xl dark:text-white mb-12">
{frontMatter.title}
</h1>
{frontMatter.description && (
<p className="text-xl mb-4">{frontMatter.description}</p>
)}
</header>
<main>
<article className="prose dark:prose-invert">{children}</article>
</main>
</article>
</Layout>
)
}
export const getStaticProps: GetStaticProps = async ({ params }) => {
if (params) {
const { source, data } = await getDocumentBySlug(params.slug as string)
const prevDocument = getPreviousDocumentBySlug(params.slug as string)
const nextDocument = getNextDocumentBySlug(params.slug as string)
return {
props: {
source,
frontMatter: data,
prevDocument,
nextDocument,
},
}
}
return { props: {} }
}
export const getStaticPaths = async () => {
const paths = documentFilePaths
// Remove file extensions for page paths
.map((path) => path.replace(/\.mdx?$/, ''))
// Map the path into the static paths object required by Next.js
.map((slug) => ({ params: { slug } }))
return {
paths,
fallback: false,
}
}

90
web/pages/index.tsx Normal file
View file

@ -0,0 +1,90 @@
import Link from 'next/link'
import { getDocuments, Document } from '../utils/mdx-utils'
import Layout from '../components/Layout'
import ArrowIcon from '../components/ArrowIcon'
import SEO from '../components/SEO'
const pages = [
{
slug: '/variables',
title: 'Variables',
desc: 'Variables let you define the various objects displayed in Conky including text, bars, graphs, and more.',
},
{
slug: '/config_settings',
title: 'Configuration settings',
desc: 'Global configuration pramaters for Conky allow you to customize various behaviours.',
},
{
slug: '/lua',
title: 'Lua API',
desc: 'Program wild things into your Conky with the Lua API',
},
]
interface IndexItemProps {
href: string
as?: string
title: string
desc?: string
}
const IndexItem: React.FunctionComponent<IndexItemProps> = (props) => {
return (
<div className="md:first:rounded-t-lg md:last:rounded-b-lg backdrop-blur-lg bg-slate-300 dark:bg-black dark:bg-opacity-30 bg-opacity-10 hover:bg-opacity-30 dark:hover:bg-opacity-50 transition border border-gray-800 dark:border-white border-opacity-10 dark:border-opacity-10 border-b-0 last:border-b hover:border-b hovered-sibling:border-t-0">
<Link
as={props.as}
href={props.href}
className="py-2 lg:py-4 px-2 lg:px-4 block focus:outline-none focus:ring-4"
>
<h2 className="text-xl md:text-2xl">{props.title}</h2>
{props.desc && <p className="mt-3 text-lg opacity-60">{props.desc}</p>}
<ArrowIcon className="mt-4" />
</Link>
</div>
)
}
interface IndexProps {
documents: Document[]
}
export default function Index({ documents }: IndexProps) {
return (
<Layout>
<SEO title="Conky" description="Conky documentation" />
<main className="w-full">
<div className="w-full">
{pages.map((p) => (
<IndexItem
key={p.slug}
href={p.slug}
title={p.title}
desc={p.desc}
/>
))}
{documents.map((document) => (
<IndexItem
key={document.filePath}
as={`/documents/${document.filePath.replace(/\.mdx?$/, '')}`}
href={`/documents/[slug]`}
title={document.data.title ?? 'Untitled'}
desc={document.data.description}
/>
))}
<IndexItem
href="https://github.com/brndnmtthws/conky/wiki"
title="Wiki"
desc="The Wiki (hosted on GitHub) contains a number of user configs, Lua scripts, FAQs and more."
/>
</div>
</main>
</Layout>
)
}
export function getStaticProps() {
const documents = getDocuments()
return { props: { documents } }
}

30
web/pages/lua.tsx Normal file
View file

@ -0,0 +1,30 @@
import Layout from '../components/Layout'
import SEO from '../components/SEO'
import { getLua, Documentation, filterDesc } from '../utils/doc-utils'
import Docs from '../components/Docs'
export interface LuaProps {
lua: Documentation
}
export default function Lua(props: LuaProps) {
return (
<Layout>
<SEO title="Conky – Lua API" description="Conky Lua API documentation" />
<main className="w-full pt-4">
<div>
<h1 className="text-2xl px-2 lg:px-4" data-cy="page-heading">
Lua API
</h1>
</div>
<Docs docs={props.lua} braces={false} assign={false} />
</main>
</Layout>
)
}
export async function getStaticProps() {
const lua = filterDesc(getLua())
return { props: { lua } }
}

31
web/pages/variables.tsx Normal file
View file

@ -0,0 +1,31 @@
import Layout from '../components/Layout'
import SEO from '../components/SEO'
import { getVariables, Documentation, filterDesc } from '../utils/doc-utils'
import Docs from '../components/Docs'
export interface VariablesProps {
variables: Documentation
}
export default function Variables(props: VariablesProps) {
return (
<Layout>
<SEO
title="Conky – Variables"
description="Conky object variables documentation"
/>
<main className="w-full pt-4">
<h1 className="text-2xl px-2 lg:px-4" data-cy="page-heading">
Variables
</h1>
<Docs docs={props.variables} braces={true} assign={false} />
</main>
</Layout>
)
}
export async function getStaticProps() {
const variables = filterDesc(getVariables())
return { props: { variables } }
}

8
web/postcss.config.js Normal file
View file

@ -0,0 +1,8 @@
// If you want to use other PostCSS plugins, see the following:
// https://tailwindcss.com/docs/using-with-preprocessors
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

View file

@ -0,0 +1,7 @@
import { writeSearchIndex } from '../utils/search-serde'
async function main() {
await writeSearchIndex()
}
main()

10
web/styles/globals.css Normal file
View file

@ -0,0 +1,10 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@tailwind variants;
code[class*='language-'],
pre[class*='language-'] {
font-family: 'Fira Code Variable', 'ui-monospace', 'SFMono-Regular', 'Menlo',
'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', 'monospace';
}

9
web/tailwind.config.js Normal file
View file

@ -0,0 +1,9 @@
module.exports = {
mode: 'jit',
content: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
darkMode: 'class', // or 'media' or 'class'
presets: [require('./utils/tailwind-preset')],
}

20
web/tsconfig.json Normal file
View file

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve"
},
"include": ["next-env.d.ts", "global.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
}

73
web/utils/doc-utils.ts Normal file
View file

@ -0,0 +1,73 @@
import fs from 'fs'
import path from 'path'
import yaml from 'js-yaml'
import { unified } from 'unified'
import remarkParse from 'remark-parse'
import remarkGfm from 'remark-gfm'
import remarkRehype from 'remark-rehype'
import rehypeStringify from 'rehype-stringify'
const DOC_PATH = path.join(process.cwd(), '..', 'doc')
export interface Documentation {
desc: string
desc_md: string
values: DocItem[]
}
export interface DocItem {
name: string
desc: string
desc_md: string
default: string | undefined
args: string[]
}
function getDocumentation(source: string): Documentation {
const configSettingsFile = fs.readFileSync(
path.join(DOC_PATH, source),
'utf-8'
)
const parsed = yaml.load(configSettingsFile.toString()) as Documentation
const docs = {
...parsed,
desc_md: processMarkdown(parsed.desc),
values: parsed.values.map((c) => ({
...c,
desc_md: processMarkdown(c.desc),
})),
}
return docs
}
export function filterDesc(docs: Documentation): Documentation {
return {
...docs,
desc: '',
values: docs.values.map((v) => ({
...v,
desc: '',
})),
}
}
export function getConfigSettings(): Documentation {
return getDocumentation('config_settings.yaml')
}
export function getVariables(): Documentation {
return getDocumentation('variables.yaml')
}
export function getLua(): Documentation {
return getDocumentation('lua.yaml')
}
function processMarkdown(input: string): string {
return unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype, {allowDangerousHtml: true})
.use(rehypeStringify, {allowDangerousHtml: true})
.processSync(input)
.toString()
}

113
web/utils/mdx-utils.ts Normal file
View file

@ -0,0 +1,113 @@
import fs from 'fs'
import path from 'path'
import matter from 'gray-matter'
import { unified } from 'unified'
import remarkGfm from 'remark-gfm'
import remarkParse from 'remark-parse'
import remarkRehype from 'remark-rehype'
import rehypeStringify from 'rehype-stringify'
// DOCUMENTS_PATH is useful when you want to get the path to a specific file
export const DOCUMENTS_PATH = path.join(process.cwd(), 'documents')
// documentFilePaths is the list of all mdx files inside the DOCUMENTS_PATH directory
export const documentFilePaths = fs
.readdirSync(DOCUMENTS_PATH)
// Only include md(x) files
.filter((path) => /\.mdx?$/.test(path))
export interface DocumentData {
title?: string
description?: string
indexWeight?: number
}
export interface Document {
content: string
data: DocumentData
filePath: string
}
export const getDocuments = (): Document[] => {
const documents = documentFilePaths.map((filePath) => {
const source = fs.readFileSync(path.join(DOCUMENTS_PATH, filePath), 'utf-8')
const { content, data } = matter(source)
return {
content,
data,
filePath,
}
})
documents.sort((a, b) => {
if (a.data.indexWeight > b.data.indexWeight) {
return 1
}
if (a.data.indexWeight < b.data.indexWeight) {
return -1
}
return a.data.title.localeCompare(b.data.title)
})
return documents
}
export const getDocumentBySlug = async (slug: string) => {
const documentFilePath = path.join(DOCUMENTS_PATH, `${slug}.mdx`)
const source = fs.readFileSync(documentFilePath, 'utf-8')
const { content, data } = matter(source)
const result = await unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype, {allowDangerousHtml: true})
// .use(rehypePrism)
.use(rehypeStringify, {allowDangerousHtml: true})
.process(content)
return { source: result.value, data, documentFilePath }
}
export const getNextDocumentBySlug = (slug: string) => {
const documents = getDocuments()
const currentFileName = `${slug}.mdx`
const currentDocument = documents.find(
(document) => document.filePath === currentFileName
)
if (!currentDocument) return null
const currentDocumentIndex = documents.indexOf(currentDocument)
const document = documents[currentDocumentIndex - 1]
// no prev document found
if (!document) return null
const nextDocumentSlug = document?.filePath.replace(/\.mdx?$/, '')
return {
title: document.data.title,
slug: nextDocumentSlug,
}
}
export const getPreviousDocumentBySlug = (slug: string) => {
const documents = getDocuments()
const currentFileName = `${slug}.mdx`
const currentDocument = documents.find(
(document) => document.filePath === currentFileName
)
if (!currentDocument) return null
const currentDocumentIndex = documents.indexOf(currentDocument)
const document = documents[currentDocumentIndex + 1]
// no prev document found
if (!document) return null
const previousDocumentSlug = document?.filePath.replace(/\.mdx?$/, '')
return {
title: document.data.title,
slug: previousDocumentSlug,
}
}

View file

@ -0,0 +1,9 @@
import * as fs from 'fs/promises'
import { createSearchIndex } from './search'
export async function writeSearchIndex() {
await fs.mkdir('public').catch(() => {})
await fs.mkdir('public/static').catch(() => {})
const index = createSearchIndex()
await fs.writeFile('public/static/fuse-index.json', JSON.stringify(index))
}

40
web/utils/search.ts Normal file
View file

@ -0,0 +1,40 @@
import Fuse, { FuseIndexRecords } from 'fuse.js'
import { getConfigSettings, getLua, getVariables } from './doc-utils'
export interface SearchItem {
kind: string
name: string
desc: string
}
export interface SearchIndex {
index: {
keys: readonly string[]
records: FuseIndexRecords
}
list: SearchItem[]
}
export function createSearchIndex(): SearchIndex {
const cs: SearchItem[] = getConfigSettings().values.map((v) => ({
kind: 'config',
name: v.name,
desc: v.desc.substring(0, 121),
}))
const vars: SearchItem[] = getVariables().values.map((v) => ({
kind: 'var',
name: v.name,
desc: v.desc.substring(0, 121),
}))
const lua: SearchItem[] = getLua().values.map((v) => ({
kind: 'lua',
name: v.name,
desc: v.desc.substring(0, 121),
}))
const list: SearchItem[] = [...cs, ...vars, ...lua]
return {
list,
index: Fuse.createIndex<SearchItem>(['name', 'desc'], list).toJSON(),
}
}

View file

@ -0,0 +1,79 @@
import plugin = require('tailwindcss/plugin')
import pluginTypography = require('@tailwindcss/typography')
const hoveredSiblingPlugin = plugin(function ({ addVariant, e }) {
addVariant('hovered-sibling', ({ container }) => {
container.walkRules((rule) => {
rule.selector = `:hover + .hovered-sibling\\:${rule.selector.slice(1)}`
})
})
})
module.exports = {
theme: {
fontFamily: {
sans: [
'Inter Variable',
'ui-sans-serif',
'system-ui',
'-apple-system',
'BlinkMacSystemFont',
'Segoe UI',
'Roboto',
'Helvetica Neue',
'Arial',
'Noto Sans',
'sans-serif',
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
'Noto Color Emoji',
],
serif: [
'Newsreader Variable',
'ui-serif',
'Georgia',
'Cambria',
'Times New Roman',
'Times',
'serif',
],
mono: [
'Fira Code Variable',
'ui-monospace',
'SFMono-Regular',
'Menlo',
'Monaco',
'Consolas',
'Liberation Mono',
'Courier New',
'monospace',
],
},
extend: {
typography: (theme) => ({
DEFAULT: {
css: {
code: {
'&::before': {
content: '"" !important',
},
'&::after': {
content: '"" !important',
},
},
fontFamily: theme('fontFamily.sans').join(', '),
},
},
}),
},
},
variants: {
extend: {
borderRadius: ['first', 'last'],
borderWidth: ['last', 'hovered-sibling'],
typography: ['dark'],
},
},
plugins: [hoveredSiblingPlugin, pluginTypography],
}