diff --git a/package-lock.json b/package-lock.json index 069b83a..b94c70b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,8 +7,12 @@ "": { "name": "site", "version": "0.0.1", + "dependencies": { + "iconify-icon": "^1.0.8", + "shiki": "^0.14.4" + }, "devDependencies": { - "@sveltejs/adapter-auto": "^2.0.0", + "@sveltejs/adapter-static": "^2.0.3", "@sveltejs/kit": "^1.20.4", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", @@ -488,6 +492,11 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -577,16 +586,13 @@ "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", "dev": true }, - "node_modules/@sveltejs/adapter-auto": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-2.1.0.tgz", - "integrity": "sha512-o2pZCfATFtA/Gw/BB0Xm7k4EYaekXxaPGER3xGSY3FvzFJGTlJlZjBseaXwYSM94lZ0HniOjTokN3cWaLX6fow==", + "node_modules/@sveltejs/adapter-static": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-2.0.3.tgz", + "integrity": "sha512-VUqTfXsxYGugCpMqQv1U0LIdbR3S5nBkMMDmpjGVJyM6Q2jHVMFtdWJCkeHMySc6mZxJ+0eZK3T7IgmUCDrcUQ==", "dev": true, - "dependencies": { - "import-meta-resolve": "^3.0.0" - }, "peerDependencies": { - "@sveltejs/kit": "^1.0.0" + "@sveltejs/kit": "^1.5.0" } }, "node_modules/@sveltejs/kit": { @@ -930,6 +936,11 @@ "node": ">=8" } }, + "node_modules/ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==" + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1810,6 +1821,17 @@ "node": ">=8" } }, + "node_modules/iconify-icon": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/iconify-icon/-/iconify-icon-1.0.8.tgz", + "integrity": "sha512-jvbUKHXf8EnGGArmhlP2IG8VqQLFFyTvTqb9LVL2TKTh7/eCCD1o2HHE9thpbJJb6B8hzhcFb6rOKhvo7reNKA==", + "dependencies": { + "@iconify/types": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/cyberalien" + } + }, "node_modules/ignore": { "version": "5.2.4", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", @@ -1835,16 +1857,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/import-meta-resolve": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-3.0.0.tgz", - "integrity": "sha512-4IwhLhNNA8yy445rPjD/lWh++7hMDOml2eHtd58eG7h+qK3EryMuuRbsHGPikCoAgIkkDnckKfWSk2iDla/ejg==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -1966,6 +1978,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/jsonc-parser": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", + "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + }, "node_modules/keyv": { "version": "4.5.3", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz", @@ -2709,6 +2726,17 @@ "node": ">=8" } }, + "node_modules/shiki": { + "version": "0.14.4", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.4.tgz", + "integrity": "sha512-IXCRip2IQzKwxArNNq1S+On4KPML3Yyn8Zzs/xRgcgOWIr8ntIK3IKzjFPfjy/7kt9ZMjc+FItfqHRBg8b6tNQ==", + "dependencies": { + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" + } + }, "node_modules/sirv": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.3.tgz", @@ -3221,6 +3249,16 @@ } } }, + "node_modules/vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==" + }, + "node_modules/vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==" + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index b13c44f..5bc8426 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "format": "prettier --plugin-search-dir . --write ." }, "devDependencies": { - "@sveltejs/adapter-auto": "^2.0.0", + "@sveltejs/adapter-static": "^2.0.3", "@sveltejs/kit": "^1.20.4", "@typescript-eslint/eslint-plugin": "^5.45.0", "@typescript-eslint/parser": "^5.45.0", @@ -28,5 +28,9 @@ "typescript": "^5.0.0", "vite": "^4.4.2" }, - "type": "module" + "type": "module", + "dependencies": { + "iconify-icon": "^1.0.8", + "shiki": "^0.14.4" + } } diff --git a/posts/first-post.md b/posts/first-post.md deleted file mode 100644 index 07dfc4f..0000000 --- a/posts/first-post.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: First post -description: First post. -date: '2023-4-14' -categories: - - sveltekit - - svelte -published: true ---- - -## Markdown - -Hey friends! πŸ‘‹ - -```ts -function greet(name: string) { - console.log(`Hey ${name}! πŸ‘‹`) -} -``` \ No newline at end of file diff --git a/posts/second-post.md b/posts/second-post.md deleted file mode 100644 index 0ccf65d..0000000 --- a/posts/second-post.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Second -description: Second post. -date: '2023-4-16' -categories: - - sveltekit - - svelte -published: true ---- - - - -## Svelte - -Media inside the **static** folder is served from `/`. - -![Svelte](favicon.png) - -## Counter - - \ No newline at end of file diff --git a/posts/third-post.md b/posts/third-post.md deleted file mode 100644 index a438889..0000000 --- a/posts/third-post.md +++ /dev/null @@ -1 +0,0 @@ -# third post \ No newline at end of file diff --git a/src/app.css b/src/app.css index a9adad4..d13776b 100644 --- a/src/app.css +++ b/src/app.css @@ -1,6 +1,6 @@ html { font-family: 'Inter'; - font-size: 15px; + font-size: 14px; color: white; background-color: black; } @@ -9,22 +9,48 @@ html, body { height: 100%; width: 100%; + padding: 0; + margin: 0; } -img { - border-radius: 10px; -} - -ul, -ol { - list-style: none; +h1, +h2, +h3, +h4, +h5, +h6, +p { padding: 0; + margin: 0; } li { padding-inline-start: 0; } +a { + text-decoration: underline; + color: lightgray; +} + a:hover { opacity: 0.8; -} \ No newline at end of file +} + +.prose :is(a) { + text-decoration: none; + font-weight: bold; +} + +.prose :is(a):hover { + text-decoration: underline; +} + +.prose pre { + white-space: pre-wrap; + word-break: break-word; + padding: 10px 20px; + border-radius: 5px; + tab-size: 2; + font-size: smaller; +} diff --git a/src/app.html b/src/app.html index 6a8aed0..cd48c4a 100644 --- a/src/app.html +++ b/src/app.html @@ -3,8 +3,11 @@ - + + + + %sveltekit.head% diff --git a/src/components/BlogHeader.svelte b/src/components/BlogHeader.svelte new file mode 100644 index 0000000..6cecd22 --- /dev/null +++ b/src/components/BlogHeader.svelte @@ -0,0 +1,40 @@ + + +
+ +
+ + +

Tags

+

+ Hello +

+
+ + diff --git a/src/components/CopyCodeInjector.svelte b/src/components/CopyCodeInjector.svelte new file mode 100644 index 0000000..1e14fb9 --- /dev/null +++ b/src/components/CopyCodeInjector.svelte @@ -0,0 +1,61 @@ + + +{#if copiedText} +
copied to clipboard
+{/if} + + + + diff --git a/src/components/Date.svelte b/src/components/Date.svelte new file mode 100644 index 0000000..6e4e4e0 --- /dev/null +++ b/src/components/Date.svelte @@ -0,0 +1,12 @@ + + +
+ {formatDate(value)} +
+ + diff --git a/src/components/Icon.svelte b/src/components/Icon.svelte new file mode 100644 index 0000000..4ddc4da --- /dev/null +++ b/src/components/Icon.svelte @@ -0,0 +1,13 @@ + + +{src} + + diff --git a/src/components/Image.svelte b/src/components/Image.svelte new file mode 100644 index 0000000..6659c05 --- /dev/null +++ b/src/components/Image.svelte @@ -0,0 +1,12 @@ + + +{src} + + diff --git a/src/components/Post.svelte b/src/components/Post.svelte new file mode 100644 index 0000000..334519a --- /dev/null +++ b/src/components/Post.svelte @@ -0,0 +1,58 @@ + + +
  • + +
    +

    + {post.title} +

    + +

    {post.description}

    +
    +
  • + + diff --git a/src/components/Tag.svelte b/src/components/Tag.svelte new file mode 100644 index 0000000..16ebe7f --- /dev/null +++ b/src/components/Tag.svelte @@ -0,0 +1,10 @@ + + +
    + #{category} +
    + + diff --git a/src/components/custom/img.svelte b/src/components/custom/img.svelte new file mode 100644 index 0000000..bc0b6b5 --- /dev/null +++ b/src/components/custom/img.svelte @@ -0,0 +1,19 @@ + + +
    + +
    + + diff --git a/src/components/custom/index.ts b/src/components/custom/index.ts new file mode 100644 index 0000000..a405b98 --- /dev/null +++ b/src/components/custom/index.ts @@ -0,0 +1,3 @@ +import img from './img.svelte'; + +export { img }; diff --git a/src/components/footer.svelte b/src/components/footer.svelte index 0c6862e..15c9078 100644 --- a/src/components/footer.svelte +++ b/src/components/footer.svelte @@ -11,7 +11,6 @@
    {new Date().getFullYear()} © - { @@ -57,10 +56,11 @@ p { margin: 0; + font-size: small; + text-align: justify; } footer { padding-block: 15px; - border-top: 1px solid; } diff --git a/src/components/header.svelte b/src/components/header.svelte index d4b4b97..eae7b36 100644 --- a/src/components/header.svelte +++ b/src/components/header.svelte @@ -5,8 +5,8 @@ diff --git a/src/lib/config.ts b/src/lib/config.ts index a156002..43e21a3 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,7 +1,8 @@ -import { dev } from "$app/environment" +import { dev } from '$app/environment'; -export const title = 'swaptr' -export const author = 'Swapnil Tripathi' -export const description = "This was supposed to be fun and it's not fun because it's too hard." -export const license = 'The MIT License (MIT)' -export const url = dev ? 'http://localhost:5173/' : 'swaptr.dev' +export const title = 'swaptr'; +export const author = 'Swapnil Tripathi'; +export const description = "This was supposed to be fun and it's not fun because it's too hard."; +export const license = 'The MIT License (MIT)'; +export const url = dev ? 'http://localhost:5173/' : 'swaptr.dev'; +export const bitcoinAddress = 'bc1q950fcdzn54u3twu5cqdgdyfwm9rdpul4x5evms'; diff --git a/src/lib/types.ts b/src/lib/types.ts new file mode 100644 index 0000000..abd50c7 --- /dev/null +++ b/src/lib/types.ts @@ -0,0 +1,11 @@ +export type categories = 'sveltekit' | 'svelte'; + +export type Post = { + title: string; + slug: string; + description: string; + image: string; + date: string; + categories: string[]; + published: boolean; +}; diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..5a2d548 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +type DateStyle = Intl.DateTimeFormatOptions['dateStyle']; + +export function formatDate(date: string, dateStyle: DateStyle = 'medium', locales = 'en') { + const formatter = new Intl.DateTimeFormat(locales, { dateStyle }); + return formatter.format(new Date(date)); +} diff --git a/src/mdsvex.svelte b/src/mdsvex.svelte new file mode 100644 index 0000000..58d0e01 --- /dev/null +++ b/src/mdsvex.svelte @@ -0,0 +1,6 @@ + + + diff --git a/src/posts/contributing_to_lndart_cln.md b/src/posts/contributing_to_lndart_cln.md new file mode 100644 index 0000000..9d19169 --- /dev/null +++ b/src/posts/contributing_to_lndart_cln.md @@ -0,0 +1,52 @@ +--- +title: 'Contributing to lndart.cln' +description: 'This blog post elaborates on how to contribute to the lndart.cln repository. We will list everything you need to know for maintaining and housing the repository.' +date: '2022-08-14' +categories: [lightning-network] +published: true +image: '/images/sob.png' +--- + +[lndart.cln](https://github.com/dart-lightning/lndart.cln) is a Dart framework for Core Lightning. This package is really handy if you wish to interact with your lightning node and develop some cool plugins for it. This post offers detailed guide on how you can start contributing to the lndart.cln package. + +The only requirement is to have a working Dart SDK. You can follow instructions from [here](https://dart.dev/get-dart) to get up and running. + +To get started, one should first fork and clone the repository [lndart.cln](https://github.com/dart-lightning/lndart.cln). The instructions on doing that can be found [here](https://docs.github.com/en/get-started/quickstart/fork-a-repo). + +Let's first understand the structure of this repository. + +The repository is structured as a monorepo which means it houses several related tools together, four in this case: + +```bash +packages +| +└─── cln_common +| +└─── cln_plugin +| +└─── lnlambda +| +└─── rpc +``` + +- The [lndart.cln_common](https://github.com/dart-lightning/lndart.cln/tree/main/packages/cln_common) package houses all the utilities and interfaces for other subsidiary packages to use. +- [lndart.cln_plugin](https://github.com/dart-lightning/lndart.cln/tree/main/packages/cln_plugin) is a Dart library that provides ergonomic support to developing custom plugins for Core Lightning. It provides comprehensive and robust support to developing plugins using functional and object-oriented methods. +- [lnlambda](https://github.com/dart-lightning/lndart.cln/tree/main/packages/lnlambda) is a minimal interface to run lnlambda function with Dart. +- The [lndart.rpc](https://github.com/dart-lightning/lndart.cln/tree/main/packages/rpc) package is a cool tool to interact with the Core Lightning RPC interface. + +If you are looking to contribute to the repository, it is highly recommended that you go through the [Hacking Guide](https://docs.page/dart-lightning/lndart.clightning/dev/MAINTAINERS). + +This is a well documented package with essential examples guiding you along the way in case you get stuck. The documentation can be found [here](https://docs.page/dart-lightning/lndart.clightning). + +You can use any IDE or text editor of your choice to hack the codebase or develop plugins. A good recommendation would be [IntelliJ IDEA](https://www.jetbrains.com/idea/) with the [Dart plugin](https://plugins.jetbrains.com/plugin/6351-dart) installed. + +It is expected that any patches submitted are well formatted. To ensure this you should run `make fmt` inside the package directory. You can also use the dart formatter for this by running `dart format file-name`. + +All code must be written with the expectation of a successful release. As with **lndart.cln**, we have the possibility to release the packages either individually or simultaneously. To make our lives easier, we can generate the changelog before a release with a minimal overhead using [changelog.dart](https://github.com/vincenzopalazzo/changelog.dart). +The instructions on how to make a release can be summarized as following: + +1. Bump the version number in the package and the `changelog.json` according to the package versioning [guide](https://dart.dev/tools/pub/versioning). +2. Generating the changelog for the package(s) is essential for every release. To generate the changelog you can refer to [here](https://docs.page/dart-lightning/lndart.clightning/dev/MAINTAINERS#how-to-make-the-release). +3. Make a GitHub release using the following tags: + - **Minor release**: `{package_name}-v{version_number}` + - **Major release**: `v{version_number}` diff --git a/src/posts/gsoc_community_bonding.md b/src/posts/gsoc_community_bonding.md new file mode 100755 index 0000000..639ffbf --- /dev/null +++ b/src/posts/gsoc_community_bonding.md @@ -0,0 +1,23 @@ +--- +title: 'GSOC: Community Bonding' +date: '2021-06-07' +author: 'swaptr' +description: 'This blog post elaborates my experience during the community bonding period of the Google Summer of Code 2021 at KDE Community.' +categories: [gsoc, kde] +published: true +image: '/images/gsoc.png' +--- + +Hello everyone! +So what a way to start the community bonding period. Kasts just got it's new logo, isn't that great!. The svg render was facing some problems in the beginning but finally we no longer share the Alligator logo, we got here, yay! + +So, Community Bonding, Now for people who just had a poof! moment and who don't quite have the idea of how things work in GSoC, we are supposed to provide our communication details in our proposals and that includes your IRC nick, phone number, email, among other things. During the community bonding period the idea is to have friendly interactions with the team and the mentors in our IRC's and chatrooms usually discussing the app with a few memes or jokes here and there. +So KDE for the most part uses Matrix as the official chat system for communication.These matrix rooms can be bridged to other protocols such as the IRC or Telegram which further helps in communication. Since the major discussions related to Kasts was previously done on the Plasma Mobile room, we decided it would be better if we better move to a new one. +So in its essence another focus of the Community Bonding is to basically get started with your project, and that could be in any way whether it be building on your proposal, incorporating new ideas, or even reset your deadlines and planning new features and building up on it. Since we had the Alligator/Kasts split right off, I had to re-visit all my deadlines and project goals. A large part of my proposal was to work on the frontend of the app and KDE has a very cool way of approaching this. We have a team of visual designers and developers who work on keeping Plasma in a great shape and visually refreshing as always. The VDG as it is called, can be summoned at will on some project or issue, and will provide you with the details you probably would have overlooked, even more so in the case of convergent applications where you are bound to make more mistakes since now you have to look for developing for both desktop and mobile devices. +My work in GSoC requires me to interact with the VDG as well, so on my mentor's advice I contacted the VDG and introduced my project there as well with some screenshots and mockups from my proposal. +The very next day, I was introduced to the first bug. It was relatively easier to spot and reproduce. Now to understand the bug, we will have to understand how things work in Kasts' core RSS/Atom parser API, [Syndication](https://github.com/KDE/syndication). On an abstract level you have what is called a Feed. Feeds are basically an ordered list of articles along with some metadata such as the title, homepage, etc. Feeds have this list of Entries, which can be understood as an individual article for the feed and in the case of podcasts this is what will provide us with our media files, called Enclosures. Seems pretty clear, doesn't it? On the most basic level, all we have to do is to fetch these enclosures and with the metadata and to populate lists of these Feeds. +Now coming back to the bug I was talking about earlier, Removing a feed is handled by the DataManager::removeFeed(), which in turn iterates over the individual entries and in turn would remove them. The DataManager::removeFeed() would return a Segmentation Fault when removing a feed that has one of its enclosure currently playing. The cause for this segfault would be to delete the enclosures and object entries and not checking first whether it would be a currently playing entry or not. Pretty basic stuff, but I was able to assimilate it clearly only after Bart explained it for me. He is really good at explaining things and to an excellent detail. While that may have been a pretty basic fix, we ran into another issue that would take up our next few days. I was able to put together a small fix for this only after Bart finished his work of changing AudioManager class' ownership back to C++. +For the next few days I would keep staring the giant GStreamer codebase, A large part of which would be to successfully build any QML app that is able to stream a media file from the internet. I would also mockup a QML app that is able to stream media using the VLC-qt library. For the time being we had to go with GStreamer for streaming support, for now. +Moving from the backend to the frontend, Kasts had a very basic look for the desktop app. we had a very simple bottom-mounted media bar which when you look at it, is very mobile-ish. We had to change that. Bart and I decided to check what options we had and with some imputs I started crafting a newer and fresher top-mounted media bar that would enclose the media details and controls. At first I started creating custom components taking inspirations from another gorgeous KDE app, Elisa. The HeaderBar did come out fine but on a closer look, it looked very similar to one of the header's Bart derieved earlier which I felt could be reused here, so I did. As this would be a relatively bigger visual change, I had to consult the VDG who advised me about some intricacies related to it. One of the developers from the VDG, Matthis decided to jump the wagon and started preparing new mockups and I would end up framing a new header, which we all felt was much refreshing and cleaner. (The old mockup still rests on one of the branches on my fork which I check-out every then and now :P) +With the desktop app in a better state (and I having something conclusive to show :P) we could safely move to discussing the hotter items in the list, that is the Content Discovery and background update of tasks. We had several discussions on which API's to use, consider there were a handful such as gPodder, iTunes, feeddirectory.org, etc. I prepared some mockups and presented them to VDG and to other developers. +Later that week Kasts was moved to the kdereview, which means the maintainers would have to present it to the KDE Community for minimum standard check which is great because now we will know exactly what the app may be missing, or thing we have overlooked and the features we need to add. diff --git a/src/posts/gsoc_onboarding.md b/src/posts/gsoc_onboarding.md new file mode 100755 index 0000000..1a76c9f --- /dev/null +++ b/src/posts/gsoc_onboarding.md @@ -0,0 +1,19 @@ +--- +title: 'GSOC: Onboarding' +date: '2021-05-20' +author: 'swaptr' +description: 'This blog post describes my onboarding at the Google Summer of Code 2021 at KDE Community.' +categories: [gsoc, kde] +published: true +image: '/images/gsoc.png' +--- + +Hello everyone! I will be participating in the Google Summer of Code 2021. I will be working on adding podcast support to [Kasts](https://summerofcode.withgoogle.com/projects/#6682134443458560). +Originally I was supposed to add podcast support to a different KDE app, [Alligator](https://apps.kde.org/alligator/). We had several discussions on the Plasma Mobile matrix room and the sole merge request with a basic podcast support which had huge improvements added by Bart De Vries was ultimately split into another app, [Kasts](https://apps.kde.org/kasts/). +This project will be mentored by Tobias Fella and Devin Lin, two of the core developers at KDE Community. It was really nice of Bart De Vries, who so graciously decided to help me with my project. +Now, a little overview of the project and the app in general. +The goal of this GSoC project is to work on the application Kasts which is a convergent podcast player, it works both on desktop and mobile devices thanks to the QtQuick and the Kirigami library. The major part of the project is to work on the UI and to provide a seamless experience to Kasts so it could work on all sorts of devices. +One of the more important features is to add support for discovering podcasts over the internet, currently we have decided to use the [GPodder Search API](https://gpodder.net/search/). Addition of this would mean Kasts could work as a full blown podcatcher. +Later I will work on adding Platform Integration for Kasts and providing android notifications as well. +I am really excited to spend this summer working with my mentors on this awesome project. +Thanks. diff --git a/src/posts/gsoc_second_phase.md b/src/posts/gsoc_second_phase.md new file mode 100755 index 0000000..940208b --- /dev/null +++ b/src/posts/gsoc_second_phase.md @@ -0,0 +1,16 @@ +--- +title: 'GSOC: Second Phase' +date: '2021-08-22' +author: 'swaptr' +description: 'This blog post comprises of the experience of building Kasts during the second stage of Google Summer of Code 2021 at KDE Community.' +categories: [gsoc, kde] +published: true +image: '/images/gsoc.png' +--- + +Hey everyone, it's been more than a month since I last wrote. This part of my GSoC project had to do more with reading and understanding than just coding. We continue from the last where we left off, I had to start with the more challenging issue of providing android integration to Kasts and adding chapter markers. While the latter was pretty easy, the former was something I was dreading from the beginning as I have zero experience with Android programming. +The android platform is HUGE and it was a pretty daunting task at first to get started with, knowing that I have to write production-level code in only a few weeks and on a topic that is so new to me. The first thing I did was to interact with my mentor on what is being expected out of it. So I was tasked with creating a media session for android. Kasts was already ported to android but when there is media content playing on android, there is just no way to control it from outside the app. This is where media sessions come in. They provide a universal way to interact with the underlying audio player. Media sessions are to Android what MPRIS is for Linux. A media session allows an app to expose playback externally and to receive commands in the form of physical buttons pressed (play/pause button on an earpiece or TV remotes or the Google assistant). +The android documentation does a great job in explaining what needs to be done to create a media session, creating an instance of MediaSessionCompat for the basic session (For Kasts we don’t really need the MediaControllerCompat) and then implementing the callbacks (onPause(), onPlay(), etc) to control the playback. In retrospect, this part was confusing me for some reason, what I was failing to understand was that the media session does not manage the media player. The player and the session ought to be different components and should be managed separately. +So I started working on this relatively late, partly because I had a lot of android stuff to read before I could even start with this. In the meantime I started a small android personal project to boost my learning, nothing fancy, just a basic calculator, but it works well and it's my first android app so that was good. I was able to create a session successfully but for some reason I was not able to show the notification. This is where my mentor Tobias helped me with a very important patch of adding the notification. Once we got that working, I started working on making the session interact with our underlying C++ media player over the JNI. So the way it works is that we have two classes in the android support library to maintain the playback state and metadata, ie. the PlaybackStateCompat and the MediaMetadataCompat. We use an object of the former to describe the media state and of the latter to describe the metadata of the content playing. We send requests from the android server side to the C++ client to do all the playback changes according to the session state and which enables the client to inform the android(java) side of the metadata changes accordingly. It makes sense once you get the hang of it, which clearly it didn't for me ;) +After some tries, it started working fine. The client and the server would remain in sync and the metadata would be preserved according to the state so that would turn out to be fine. There were some features that could not be completed like Seeking which I plan on finishing after GSoC. +Now the last issue on the list was adding the podcast chapter markers. Chapter marks are clickable points used to break an audio/video into sections and they let users jump to a specific time of the media. I used the podlove simple chapters, a formal to extend Atom and RSS feeds to define chapter structures. The task was accomplished by first fetching everything that represents a chapter and adding it to the database with their title, name, start time, link, image and then using a model to list them according to the podcast id. diff --git a/src/posts/gsoc_week_four_five.md b/src/posts/gsoc_week_four_five.md new file mode 100755 index 0000000..34d58e0 --- /dev/null +++ b/src/posts/gsoc_week_four_five.md @@ -0,0 +1,15 @@ +--- +title: 'GSOC: Week Four and Five' +date: '2021-07-12' +author: 'swaptr' +description: 'This blog post sums up my week 4 and 5 at KDE Community during the Google Summer of Code 2021.' +categories: [gsoc, kde] +published: true +image: '/images/gsoc.png' +--- + +So back from where we left off, I completed majority of work on the other branches. Don't be fooled from my casual attitude towards things, the work was still important since we had to add tooltips to various controls and keyboard navigation. Now the tooltips part was relatively simpler, both in coding as well as in reviewing, but for simplicity I merged both the issues into one. So at first I started rebasing these branches and taking away stuffs to their respective branches and creating separate merge requests in the process. With that done and ready to be reviewed, we can move to the errors on the work/podcastsearchmodel branch. I had to finish the API with more role names and incorporating it all into the QML side of things. That required an extensive internal debate as to using a dialog to display the feed overviews. My initial idea was to use a simple dialog to display the metadata for the podcast and for the mobile I could add a drawer that would pop up everytime the user wants to glance at what the podcast has to offer. Bart brought this to my attention that we could reuse the Feed details page entirely if there were some critical changes done very cheekily. Plus it would remain consistent with the app behavior to push pages when we wish to look at the podcast details. So that did sound like a valid suggestion and I got to working. Scrapped the code for drawers and dialogs and refactored the Feed details page and voila, its working great and looks wonderful too. :) +Later this week there were several issues raised on invent, 4 in fact, namely the one with the user should be able to change the storage location for the downloaded enclosures, content sync between devices, slowing the playback speed. So I decided to start working on one of those issues, "slowing the playback rate". I added a new Merge Request for the same and the it was merged. +My tasks from next week would be to start working on Android platform integration and I am really excited for it. +This week was a build up towards my second phase of GSoC which would be to add Android Platform Integration. So to begin with, I now have to move from compiling in my host machine to the exciting world of Docker. I started working on a feature for Kasts. The feature would allow to display a list of user's search history on the Discover page. The trick is just to make it a persistent list model which can then be used to populate the views. This being a relatively easier issue and something that I have previously worked on, didn't take much time. Now I am slightly unsure of the scope of this feature but we see it in every media and podcatcher so I thought it would be a good addition to Kasts. I also modified the model for basic interaction by the user, like to clear the search history and then did create a new merge request for the same. +The next week would be evaluations and I feel confident with my work. diff --git a/src/posts/gsoc_week_one.md b/src/posts/gsoc_week_one.md new file mode 100755 index 0000000..e3dc9d3 --- /dev/null +++ b/src/posts/gsoc_week_one.md @@ -0,0 +1,15 @@ +--- +title: 'GSOC: Week One' +date: '2021-06-14' +author: 'swaptr' +description: 'This blog post sums up my first week at KDE Community during the Google Summer of Code 2021.' +categories: [gsoc, kde] +published: true +image: '/images/gsoc.png' +--- + +So last blog we finished on the kdereview thing. I remind you this because a lot of work this week would end up being related to issues raised in the kdereview. +We started by fixing issues with the color of the HeaderBar, a lot of which were basic fixes in accordance to KDE visual guidelines. Now that we had people from the VDG involved, we could now hope to better insights into the graphical aspects of the app. Another fix that I had to add was to shrink the sidebar when the app is in a narrow mode and expand otherwise. +To get started with the Streaming support, I decided to use the GStreamer package and tried building on of its QML examples, to run the example, I had to set up a custom GStreamer build which I must tell is a pain. +I then had to start working on one of the kdereview issues which was to highlight the currently opened page. Now this would be a simple fix but we decided to venture into a better and more effective method page handling. Initially the solution was to use a simple Kirigami Pagepool, pushing and popping page at will, and just keeping track of the currentPage, but we could not incorporate an effective highlighting strategy here. So I decided to spring up a more "smarter" (i am guessing :P) way of only pushing-only-the-pages-that-would-make-sense, in this way we would have the reference to the base page and we could highlight it as per the users choice. So the catch here is that the pages related to individual feeds need you provide the feed reference as a required property, same as with the EntryPages. So now, not only would we need to have a reference to the Root page, but also to the individual Feed and Entry pages, and then to the pages themselves. This was done by implementing two functions getPage() and pushPage(), where the former would return an instance of the page, and the latter would take arguments in the form of a list of pages and their properties and then accordingly push those pages onto the stack. With hindsight the subsequent week was denoted to successive refactorings to keep in check for several properties. +Later this week I started looking into the content discovery issue and designing the frontend of the application. We are using the GPodder Search API so we would need some way to manipulate the .OPML files returned from the search, I had inputs from Bart and Tobias as to how to prepare the C++ backend of the code and I prepared another MR with tooltips and keyboard navigation. So the progress is slightly slower for now but its only the start and I expect to pick the pace soon enough. :) diff --git a/src/posts/gsoc_week_three.md b/src/posts/gsoc_week_three.md new file mode 100755 index 0000000..5c8c749 --- /dev/null +++ b/src/posts/gsoc_week_three.md @@ -0,0 +1,17 @@ +--- +title: 'GSOC: Week Three' +date: '2021-07-01' +author: 'swaptr' +description: 'This blog post highlights my third week at KDE Community during the Google Summer of Code 2021.' +categories: [gsoc, kde] +published: true +image: '/images/gsoc.png' +--- + +Just one of those days when you wake up and feel like "I knew there was something wrong, or something way too easy". It seemed like an average task but I was sure I was missing something. +So what am I talking about??!! +Well, GPodder is a pretty basic API and hence it does not offer you anything too fancy, and hence from all that I could use, it still felt that page is pretty minimal since the search delegate could not be populated with anythingmore than the podcast name in all honesty, this is too bad since I was expecting it to atleast return some images and more metadata. I could see that the delegate was pretty empty and it could use some fancy editing. In this quest I decided to create individual XmlListModel's for each xmlUrl and return some metadata from it (you can already feel how tiresome and sloooowwwww it would be, which was exactly the case.). Each ping would take several seconds to display the most basic data, before it fetches the images and everything, and we are talking about almost 10 seconds in preparing each request, which was like too much. Even more when you think that I was planning to implement some carousels with recommendations and trending lists, and with a 10+ second overhead, and each search returning 20 results on an average would all load just too slow. We decided maybe there was a need of a better API and we should probably start exploring options. :( +So yes, it seems the previous week's progress would have to be junked, maybe we could use the frontend but the business logic of all those XmlListModel's would have to replace with a better solution. We had several API's available to us from the start, and from all of our option, [Fyyd](https://github.com/eazyliving/fyyd-api#search) and [PodcastIndex](https://podcastindex.org/). Eventually it would be PodcastIndex which also has a secure request authorization. +At this time, I started working on preparing the android development environment for KDE apps. Bart de Vries and Tobias Fella were very helpful (like they always have been throughout the timeline) in dictating each and every step meticulously. Creating APK's of KDE apps is done through android docker environment and since I am relatively new to docker, initially I had trouble following, but it was made easier by luculent steps laid out by Bart, he even gave his own scripts and I firmly believe one can create a wonderful tutorial out of it, it was so easy to follow along. Thanks to Tobias Fella, I could also setup the newer way of building apks using our own build system and package manager [Craft](https://community.kde.org/Craft). +So later this week after taking a couple of days off, I decided to start working on a newer model. Tobias Fella had already added the part of the model where we make a network request using a search URL and then all I had to do is to store the data by creating some custom roles. This would mean, I have to reuse the frontend from the previous model and simply connect the search button to the new model and then use the stored results to populate the the result listview and the FeedOverviewDrawers. This was all relatively easy and since we were the ones writing the code, it was much more flexible than readymade models. +So it went well and I got my code changes reviewed, I still have a few issues to fix and some old MR's to complete which I had almost forgotten for the next week but nothing too serious for now. The difficult part has been setting up this Jekyll site for now on my main Arch system, it runs into gem errors every then and now or some issues regarding image renders and then all I am left with is to start over again :D. diff --git a/src/posts/gsoc_week_two.md b/src/posts/gsoc_week_two.md new file mode 100755 index 0000000..9794c54 --- /dev/null +++ b/src/posts/gsoc_week_two.md @@ -0,0 +1,19 @@ +--- +title: 'GSOC: Week Two' +date: '2021-06-21' +description: 'This blog post intricates my second week at KDE Community during the Google Summer of Code 2021.' +categories: [gsoc, kde] +published: true +image: '/images/gsoc.png' +--- + +After few regressions and hours of fixing issues I got the headerbar and sidebar revamp MR merged so we start the week on a high and more than that we had the first official build of Kasts, yay! +Sprucing up my content discovery this week and now with a plan to tackle it, I was able to use a [XmlListModel](https://doc.qt.io/qt-5/qml-qtquick-xmllistmodel-xmllistmodel.html) and parse the OPML file and was able to put together a MR for the same. I was supposed to add a C++ one instead, but the XmlListModel seemed like a perfect solution since it was supposed to be a read-only model and it was relatively easier to use (eh, lazy). +Now lets discuss a bit what a model is and how the XmlListModel works in Kasts. +So a model is basically a adaptor class which is used to access structured data. Custom models can be subclasses of QAbstractItemModel, and we need to expose data at hand using a unified API. There are several QML types for eg the List Model, XML Modeln Integer Models, Object Models etc. and also you can create your own C++ data models that are used with Views such as ListView, GridView, PathView, Repeater, TableView, etc. to visualize the contents of these models and using a delegate to draw a single entry of data and interact with it. +So the OPML file we were importing will contain our actual data. We will need a model to sort of wrap around the data and make it available for us in the form of roles and then ultimately display as a list for the users to check the search results. Remember that the data is not stored inside the model per se, the data is just provided to the model and its the models job to adapt to that data so that it can be used with a view. +After that exposing the data to the XmlListModel is pretty easy, and I could follow along from the Qt docs. +So all I was left to do was attach the search url which is quite simply- +\*https://gpodder.net/search.opml?q=**your-search-here*** +to a TextField and a Search button, voila, you can now search for podcasts from the internet and display the results. For more convenience I also added a drawer that would display the metadata for the podcast and also a button that would simply Subscribe the required podcast. +Next week I would be looking to craft a massive discover page, even though this was not part of my GSoC timeline, I decided to start working on this because I just love QML :), I hope it to be a full blown podcast searching platform. diff --git a/src/posts/how_to_use_lndart_cln.md b/src/posts/how_to_use_lndart_cln.md new file mode 100644 index 0000000..866afe3 --- /dev/null +++ b/src/posts/how_to_use_lndart_cln.md @@ -0,0 +1,143 @@ +--- +title: 'Develop custom plugins for Core Lightning' +description: 'This blog post elaborates on how to use lndart.cln to develop your own custom plugins for Core Lightning in the Dart programming environment.' +date: '2022-08-21' +categories: [lightning-network] +published: true +image: '/images/sob.png' +--- + +Hello again, today we will look into developing custom plugins for Core Lightning using the Dart programming language. Core Lightning ships with a solid and high-performant core written in the C programming language, and extended functionality is provided through plugins. [Some](https://github.com/ElementsProject/lightning/tree/master/plugins) plugins critical to the functioning of Core Lightning are shipped along, whereas there are many [community curated](https://github.com/lightningd/plugins) plugins available as well. + +So let's get into it and start developing our next great idea. + +[lndart.cln](https://github.com/dart-lightning/lndart.cln) houses a package [cln_plugin](https://github.com/dart-lightning/lndart.cln/tree/main/packages/cln_plugin) which is used to develop custom plugins. A [template](https://github.com/dart-lightning/lndart.cln_plugin) that implements the library is available at you disposal for easy setup of your project. All efforts are made to keep the template up to date, but in case we are lagging behind, you can continue to keep using the latest library by keeping your `pubspec.yaml` up to date. + +```yaml +dependencies: + cln_plugin_api: ^0.0.1-beta.2 +``` + +The library offers you two ways to develop a plugin. The repository has branches which can help you in setting the initial configuration of your plugin for the same. + +1. Object-Oriented plugins using [template/class](https://github.com/dart-lightning/lndart.cln_plugin/tree/template/class) +2. Functional plugins using [template/function](https://github.com/dart-lightning/lndart.cln_plugin/tree/template/function) + +Once your template is set up, you can start working on implementing your plugin. The `/lib/src/` directory for the most part is what you will be working on and will contain the core implementation of your plugin. + +We can get started by extending the `Plugin` class in our code. Then the simplest thing you can do is instantiate an object of it inside the `main()` of your plugin as shown below. + +```dart +class MyPlugin extends Plugin {} + +void main() { + var plugin = MyPlugin(); + plugin.start(); +} +``` + +Code that goes inside this subclass will form the business logic of your plugin. The `start()` will be called when core lightning marks your plugin as ready and the plugin will communicates with lightningd through it's stdin and stdout. + +The `lndart.cln` API exposes several methods which can be used to build robust plugins conforming to the core lightning and JSON-RPCv2.0 standards. A plugin can interact with Core Lightning in several ways and we have methods to configure this interaction: + +- RPC command line options using the `registerOption()`. +- RPC methods using the `registerRPCMethod()`. +- RPC Hook calls using the `registerHook()`. +- Subscriptions using `registerSubscriptions()`. + +Lets understand how we can use these methods in detail. + +### 1. registerOption() + +Core Lightning allows plugins to register their own command line options which are exposed through the lightning daemon i.e lightningd. + +```bash +lightningd --my_cli_option +``` + +A typical registerOption() looks something like this and as you can see most of the fields are self explanatory. + +```dart +registerOption( + name: "my_option", + type: "string", + def: "hello this string represents the default my_option value.", + description: "This is an example of how the option looks like"); +``` + +When we start lightningd by enabling this plugin, you'll notice we can now use the `--my_option=` option to assign a new value to it, i.e. `--plugin="This is a new value for this option"`. + +### 2. registerRPCMethod() + +This is probably the most versatile method in your arsenal. The registerRPCMethod() can be used to invoke a custom dart callback at any time. Yes, you heard it right. The applications are endless and it can be tailored to suit your need. You can return custom responses by interacting with core lightning. The is a minimal overhead to set this up. + +```dart +registerRPCMethod( + name: "my_custom_method", + usage: "", + description: "This string is the description of my custom RPC method.", + callback: (plugin, request) => myCustomMethod(plugin, request)); +``` + +We use the `callback` field to assign a callback to our RPC method. A simple callback would look like this. + +```dart +Future> myCustomMethod(Plugin plugin, Map request) { + log(level:'debug', message:"This is a successful method call."); + return Future.value({ + "my_custom_option": getOpt(key: "my_option") ?? "Option not registered!", + }); +} +``` + +In this case we use the `getOpt()` to return the value of the option we registered earlier or a default placeholder string "Option not registered!" if it was not found for some reason. + +### 3. registerHook() + +Hooks allow a plugin to define custom behavior for lightningd without modifying the Core Lightning source code itself. The below code snippet registers a hook that is triggered by core lightning when any rpc command is executed. + +```dart +registerHook(name: "rpc_command", callback: onCustomRPCCommand); +``` + +This call registers a callback `onCustomRPCCommand` to log into Core Lightning but can be customised with new behavior. + +```dart +Future> onCustomRPCCommand( + Plugin plugin, Map request) { + log(level: "info", message: "This is a hook callback!"); + return Future(() => {"result": "continue"}); +} +``` + +### 4. registerSubscriptions() + +Subscriptions allow plugins to respond using custom callbacks to Core Lightning notifications. We can assign a callback function with custom behavior to any of the internal notifications in a very simple and push-based mechanism. + +```dart +registerSubscriptions( + event: 'connect', + callback: customNotificationMethod); +``` + +The above call would make the `customNotificationMethod()` to be registered as a callback to whenever a new connection to a peer is established. + +```dart +Future> customNotificationMethod( + Plugin plugin, Map request) { + log(level: 'debug', message: 'Notification received!'); + return Future.value({}); +} +``` + +Plugins can be written in any programming language and can be compiled into an executable and run at the startup of lightningd using the `--plugin=` option to register the plugins that should be started. + +In dart we can compile our plugin into an executable in this way. + +```bash +dart compile exe path_to_the_main_file +``` + +This executable can then be run during the startup of lightningd using the `--plugin=` option. + +This is perhaps everything you need to know to start developing a plugin for Core Lightning in Dart. diff --git a/src/routes/+error.svelte b/src/routes/+error.svelte new file mode 100644 index 0000000..26d5b51 --- /dev/null +++ b/src/routes/+error.svelte @@ -0,0 +1,19 @@ + + +
    +

    {$page.status}

    + {$page.error?.message} +
    + + diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 4e42ab3..1b784a6 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -2,12 +2,17 @@ import Header from '../components/header.svelte'; import Footer from '../components/footer.svelte'; import '../app.css'; + import PageTransition from './transition.svelte'; + + export let data;
    - + + +
    @@ -19,16 +24,39 @@ display: grid; grid-template-rows: auto 1fr auto; margin-inline: auto; - padding-inline: 2rem; + padding-inline: 1rem; } main { - padding-block: 4rem; + margin: 0rem 0rem; + } + + @media (min-width: 24rem) { + main { + margin: 0rem 1rem; + } + } + + @media (min-width: 36rem) { + main { + margin: 0rem 1.5rem; + } + } + + @media (min-width: 48rem) { + main { + margin: 0rem 2rem; + } } - @media (min-width: 1280px) { + @media (min-width: 80rem) { .layout { padding-inline: 0; + margin-inline: auto; + } + + main { + margin: 0.5rem 4rem; } } diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts new file mode 100644 index 0000000..0d87b6b --- /dev/null +++ b/src/routes/+layout.ts @@ -0,0 +1,7 @@ +export const prerender = true; + +export async function load({ url }) { + return { + url: url.pathname + }; +} diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index fa17b22..8b69bff 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1 +1,7 @@ -

    Welcome to SvelteKit

    + + + + Home | {config.title} + diff --git a/src/routes/about/+page.svelte b/src/routes/about/+page.svelte new file mode 100644 index 0000000..6a0581e --- /dev/null +++ b/src/routes/about/+page.svelte @@ -0,0 +1,153 @@ + + + + About | {config.title} + + +
    + +

    + Hello, my name is Swapnil. I am an open source engineer based in India. Presently I am working + on cloud technologies and blockchains. +

    +
    + + + + + + + + + + + + + + + + + + + + + + +
    +

    Socials:

    + +

    Sponsor:

    + +
    + + +

    Sponsor

    + +

    + + {config.bitcoinAddress} + +

    +
    + + diff --git a/src/routes/api/posts/+server.ts b/src/routes/api/posts/+server.ts new file mode 100644 index 0000000..add2ec3 --- /dev/null +++ b/src/routes/api/posts/+server.ts @@ -0,0 +1,33 @@ +import type { Post } from "$lib/types" +import { json } from '@sveltejs/kit' + +async function getPosts() { + let posts: Post[] = [] + + const paths = import.meta.glob('/src/posts/*.md', { eager: true }) + + for (const path in paths) { + const file = paths[path] + const slug = path.split('/').at(-1)?.replace('.md', '') + + if (file && typeof file === 'object' && 'metadata' in file && slug) { + const metadata = file.metadata as Omit + const post = { + ...metadata, + slug + } satisfies Post + post.published && posts.push(post) + } + } + + posts = posts.sort( + (first, second) => new Date(second.date).getTime() - new Date(first.date).getTime() + ) + + return posts +} + +export async function GET() { + const posts = await getPosts() + return json(posts) +} diff --git a/src/routes/blog/+layout.svelte b/src/routes/blog/+layout.svelte new file mode 100644 index 0000000..7bce1ca --- /dev/null +++ b/src/routes/blog/+layout.svelte @@ -0,0 +1,13 @@ + + + + {config.title} + + +
    + + +
    diff --git a/src/routes/blog/+page.svelte b/src/routes/blog/+page.svelte new file mode 100644 index 0000000..bd7e4ec --- /dev/null +++ b/src/routes/blog/+page.svelte @@ -0,0 +1,44 @@ + + + + Blog | {config.title} + + +
    +
      + {#if data.posts.length === 0} +
      + No posts found +
      + {:else} + {#each data.posts as post} + + {/each} + {/if} +
    +
    + + diff --git a/src/routes/blog/+page.ts b/src/routes/blog/+page.ts new file mode 100644 index 0000000..0adbe4d --- /dev/null +++ b/src/routes/blog/+page.ts @@ -0,0 +1,7 @@ +import type { Post } from "$lib/types" + +export async function load({ fetch }) { + const response = await fetch('api/posts') + const posts: Post[] = await response.json() + return { posts } +} diff --git a/src/routes/blog/[slug]/+page.svelte b/src/routes/blog/[slug]/+page.svelte new file mode 100644 index 0000000..eee64a2 --- /dev/null +++ b/src/routes/blog/[slug]/+page.svelte @@ -0,0 +1,72 @@ + + + + {data.meta.title} | {config.title} + + + + +
    +
    + + +
    + +
    + + + +
    +
    + + diff --git a/src/routes/blog/[slug]/+page.ts b/src/routes/blog/[slug]/+page.ts new file mode 100644 index 0000000..a8aebb9 --- /dev/null +++ b/src/routes/blog/[slug]/+page.ts @@ -0,0 +1,13 @@ +import { error } from '@sveltejs/kit'; + +export async function load({ params }) { + try { + const post = await import(`../../../posts/${params.slug}.md`); + return { + content: post.default, + meta: post.metadata + }; + } catch (err) { + throw error(404, 'Not Found'); + } +} diff --git a/src/routes/rss.xml/+server.ts b/src/routes/rss.xml/+server.ts new file mode 100644 index 0000000..9294f47 --- /dev/null +++ b/src/routes/rss.xml/+server.ts @@ -0,0 +1,37 @@ +import * as config from '$lib/config'; +import type { Post } from '$lib/types'; + +export const prerender = true; + +export async function GET({ fetch }) { + const response = await fetch('api/posts'); + const posts: Post[] = await response.json(); + + const headers = { 'Content-Type': 'application/xml' }; + + const xml = ` + + + ${config.title} + ${config.description} + ${config.url} + + ${posts + .map( + (post) => ` + + ${post.title} + ${post.description} + ${config.url}/${post.slug} + ${config.url}/${post.slug} + ${new Date(post.date).toUTCString()} + + ` + ) + .join('')} + + + `.trim(); + + return new Response(xml, { headers }); +} diff --git a/src/routes/transition.svelte b/src/routes/transition.svelte new file mode 100644 index 0000000..17c97eb --- /dev/null +++ b/src/routes/transition.svelte @@ -0,0 +1,17 @@ + + +{#key url} +
    + +
    +{/key} + + diff --git a/static/.nojekyll b/static/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/static/favicon.svg b/static/favicon.svg new file mode 100644 index 0000000..7850978 --- /dev/null +++ b/static/favicon.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/static/images/clntk.jpg b/static/images/clntk.jpg new file mode 100644 index 0000000..831a2f9 Binary files /dev/null and b/static/images/clntk.jpg differ diff --git a/static/images/gsoc.png b/static/images/gsoc.png new file mode 100644 index 0000000..e932a11 Binary files /dev/null and b/static/images/gsoc.png differ diff --git a/static/images/hyperledger.png b/static/images/hyperledger.png new file mode 100644 index 0000000..3d02016 Binary files /dev/null and b/static/images/hyperledger.png differ diff --git a/static/images/sob.png b/static/images/sob.png new file mode 100644 index 0000000..2439513 Binary files /dev/null and b/static/images/sob.png differ diff --git a/static/images/xros.png b/static/images/xros.png new file mode 100644 index 0000000..e452801 Binary files /dev/null and b/static/images/xros.png differ diff --git a/static/inter.ttf b/static/inter.ttf new file mode 100644 index 0000000..5e4851f Binary files /dev/null and b/static/inter.ttf differ diff --git a/static/qr.png b/static/qr.png new file mode 100644 index 0000000..66c26bf Binary files /dev/null and b/static/qr.png differ diff --git a/svelte.config.js b/svelte.config.js index f074ad0..61b16dd 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -1,10 +1,23 @@ -import adapter from '@sveltejs/adapter-auto'; +import adapter from '@sveltejs/adapter-static'; import { vitePreprocess } from '@sveltejs/kit/vite'; -import { mdsvex } from 'mdsvex' +import { escapeSvelte,mdsvex } from 'mdsvex' +import shiki from 'shiki'; /** @type {import('mdsvex').mdsvexOptions} */ const mdsvexOptions = { - extensions: ['.md'] + extensions: ['.md'], + layout: { + _: './src/mdsvex.svelte' + }, + highlight: { + highlighter: async (code, lang='text') => { + const hightlighter = await shiki.getHighlighter({ + theme: 'vitesse-dark' + }) + const html = escapeSvelte(hightlighter.codeToHtml(code, {lang})) + return `{@html \`${html}\`}` + } + } } /** @type {import('@sveltejs/kit').Config} */