logseq post
This commit is contained in:
parent
10426919e1
commit
0c2ac5e521
13 changed files with 1249 additions and 48 deletions
|
@ -1,13 +1,14 @@
|
||||||
import { defineConfig } from "astro/config";
|
import { defineConfig } from "astro/config";
|
||||||
import mdx from "@astrojs/mdx";
|
import mdx from "@astrojs/mdx";
|
||||||
|
|
||||||
import sitemap from "@astrojs/sitemap";
|
import sitemap from "@astrojs/sitemap";
|
||||||
|
import { astroImageTools } from "astro-imagetools";
|
||||||
|
|
||||||
import { remarkReadingTime } from "./plugin/remark-reading-time";
|
import { remarkReadingTime } from "./plugin/remark-reading-time";
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
site: "https://mzhang.io",
|
site: "https://mzhang.io",
|
||||||
integrations: [mdx(), sitemap()],
|
integrations: [mdx(), sitemap(), astroImageTools],
|
||||||
markdown: {
|
markdown: {
|
||||||
syntaxHighlight: false,
|
syntaxHighlight: false,
|
||||||
remarkPlugins: [remarkReadingTime],
|
remarkPlugins: [remarkReadingTime],
|
||||||
|
|
1061
package-lock.json
generated
1061
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -14,6 +14,7 @@
|
||||||
"@astrojs/rss": "^3.0.0",
|
"@astrojs/rss": "^3.0.0",
|
||||||
"@astrojs/sitemap": "^3.0.0",
|
"@astrojs/sitemap": "^3.0.0",
|
||||||
"astro": "^3.0.3",
|
"astro": "^3.0.3",
|
||||||
|
"astro-imagetools": "^0.9.0",
|
||||||
"fork-awesome": "^1.2.0",
|
"fork-awesome": "^1.2.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mdast-util-to-string": "^4.0.0",
|
"mdast-util-to-string": "^4.0.0",
|
||||||
|
|
|
@ -4,25 +4,29 @@ const posts = defineCollection({
|
||||||
type: "content",
|
type: "content",
|
||||||
|
|
||||||
// Type-check frontmatter using a schema
|
// Type-check frontmatter using a schema
|
||||||
schema: z.object({
|
schema: ({ image }) =>
|
||||||
title: z.string(),
|
z.object({
|
||||||
date: z.date(),
|
title: z.string(),
|
||||||
|
date: z.date(),
|
||||||
|
|
||||||
// Transform string to Date object
|
// Transform string to Date object
|
||||||
pubDate: z
|
pubDate: z
|
||||||
.string()
|
.string()
|
||||||
.or(z.date())
|
.or(z.date())
|
||||||
.transform((val) => new Date(val))
|
.transform((val) => new Date(val))
|
||||||
.optional(),
|
.optional(),
|
||||||
updatedDate: z
|
updatedDate: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.transform((str) => (str ? new Date(str) : undefined))
|
.transform((str) => (str ? new Date(str) : undefined))
|
||||||
.optional(),
|
.optional(),
|
||||||
|
|
||||||
tags: z.array(z.string()),
|
heroImage: image().optional(),
|
||||||
draft: z.boolean().default(false),
|
heroAlt: z.string().optional(),
|
||||||
}),
|
|
||||||
|
tags: z.array(z.string()),
|
||||||
|
draft: z.boolean().default(false),
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const collections = { posts };
|
export const collections = { posts };
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
---
|
|
||||||
title: Thoughts on Logseq
|
|
||||||
date: 2023-08-31T13:57:29.022Z
|
|
||||||
tags:
|
|
||||||
- organization
|
|
||||||
- logseq
|
|
||||||
draft: true
|
|
||||||
---
|
|
||||||
|
|
||||||
After working for quite a bit I like to catch up with some friends from time to
|
|
||||||
time, when I made a shocking discovery -- most of them don't really use a
|
|
||||||
calendar of any sort to manage their lives.
|
|
||||||
|
|
||||||
For a while I've always wanted a kind of personal information manager: something
|
|
||||||
that would put all my information in one place and make it easy for me to query
|
|
||||||
across apps.
|
|
BIN
src/content/posts/2023-08-31-thoughts-on-logseq/calendarHero.png
Normal file
BIN
src/content/posts/2023-08-31-thoughts-on-logseq/calendarHero.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 984 KiB |
119
src/content/posts/2023-08-31-thoughts-on-logseq/index.md
Normal file
119
src/content/posts/2023-08-31-thoughts-on-logseq/index.md
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
---
|
||||||
|
title: Thoughts on personal organization
|
||||||
|
date: 2023-08-31T13:57:29.022Z
|
||||||
|
tags:
|
||||||
|
- organization
|
||||||
|
- logseq
|
||||||
|
|
||||||
|
heroImage: ./calendarHero.png
|
||||||
|
heroAlt: pastel colored stationery background with a bunch of calendars and personal organization tools in a crayon drawing style
|
||||||
|
---
|
||||||
|
|
||||||
|
After working for quite a bit I like to catch up with some old friends from time
|
||||||
|
to time, when I made a surprising discovery -- many of them don't really use a
|
||||||
|
calendar of any sort to manage their lives. Tracking something that happens more
|
||||||
|
than a week into the future is generally out of the picture.
|
||||||
|
|
||||||
|
But I understand. Putting events into a calendar is kind of a chore. Calendars
|
||||||
|
that are [standards-compliant][3] are still primarily use email for the most part
|
||||||
|
(sending invites, updating times, etc.) and calendars that aren't
|
||||||
|
standards-compliant won't be compatible between different people unless they're
|
||||||
|
using the same service.
|
||||||
|
|
||||||
|
[3]: https://datatracker.ietf.org/doc/html/rfc5545
|
||||||
|
|
||||||
|
The personal management story has always been kind of fragmented. Calendars are
|
||||||
|
supposed to manage the entire picture of my personal schedule, yet they only see
|
||||||
|
a small slice without more information. The only things calendars can see
|
||||||
|
automatically with no intervention on my part are emails that happen to include
|
||||||
|
.ics files.
|
||||||
|
|
||||||
|
> I'm sure Google or Apple could probably ritz up their mail servers to scan text
|
||||||
|
> and try to see if there's events without there being an actual .ics file, but
|
||||||
|
> that's missing the point. The vast majority of people I associate with rarely
|
||||||
|
> sends email events anymore.
|
||||||
|
|
||||||
|
## Journals
|
||||||
|
|
||||||
|
For a while I've always wanted a kind of personal information manager: something
|
||||||
|
that would put all my information in one place and make it easy for me to query
|
||||||
|
across apps. When I embarked on this search I wouldn't have thought that the
|
||||||
|
most promising tool would end up being a journaling app.
|
||||||
|
|
||||||
|
(by journaling app I mean something like [Logseq], [Obsidian], [Notion],
|
||||||
|
[Workflowy] or [the][roam] [million][joplin] [other][craft]
|
||||||
|
[similar][stdnotes] [apps][bear] that allow you to write some markdown-ish
|
||||||
|
content, store it, and then never look back at it again)
|
||||||
|
|
||||||
|
[logseq]: https://logseq.com
|
||||||
|
[obsidian]: https://obsidian.md/
|
||||||
|
[notion]: https://www.notion.so/
|
||||||
|
[workflowy]: https://workflowy.com/
|
||||||
|
[roam]: https://roamresearch.com/
|
||||||
|
[joplin]: https://joplinapp.org/
|
||||||
|
[craft]: https://www.craft.do/
|
||||||
|
[stdnotes]: https://standardnotes.com/
|
||||||
|
[bear]: https://bear.app/
|
||||||
|
|
||||||
|
The world of journaling apps is vast but undiverse. Most of the apps just have
|
||||||
|
the same features others do, minus one or two gimmicks that makes it a ride or
|
||||||
|
die. But there's one important feature that I have started looking out for
|
||||||
|
recently: the ability to attach arbitrary metadata to journal entries and be
|
||||||
|
able to query for them.
|
||||||
|
|
||||||
|
I think the community is starting to realize that these journals are really just
|
||||||
|
databases, and extracting structured fields is extremely important if you want
|
||||||
|
any kind of smart understanding of what is being journaled.
|
||||||
|
|
||||||
|
[Logseq], the app that I've settled on, is backed by a [Datascript] store and
|
||||||
|
exposes a lot of this functionality to you as a user. It allows you to query
|
||||||
|
directly on properties that you write into your daily journal or any other page,
|
||||||
|
for example like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
- ... other content ...
|
||||||
|
- minicross:: 34
|
||||||
|
- ... other content ...
|
||||||
|
```
|
||||||
|
|
||||||
|
I use this on my daily journals to track how long it takes me to do the [NY
|
||||||
|
Times daily crossword][minicross]. But Logseq is able to index this property in
|
||||||
|
particular and let me query on it later:
|
||||||
|
|
||||||
|
[datascript]: https://github.com/tonsky/datascript
|
||||||
|
[minicross]: https://www.nytimes.com/crosswords/game/mini
|
||||||
|
|
||||||
|
![performing a query in logseq](./logseqQuery.png)
|
||||||
|
|
||||||
|
I can write todo items inline in my journal and find them all at a time as well.
|
||||||
|
As an example, here's all of the todo items that I've tagged specifically with
|
||||||
|
#read:
|
||||||
|
|
||||||
|
![reading list in logseq](./readingList.png)
|
||||||
|
|
||||||
|
The fact that it truly is a database means I can start piling things in here and
|
||||||
|
automatically perform data extraction for a more complete picture of my daily
|
||||||
|
life. In the future I'd like to do dumps for my sleep and health data as well
|
||||||
|
and have Logseq be my ultimate source of truth.
|
||||||
|
|
||||||
|
I've also started developing a [calendar plugin for Logseq][2] that will have
|
||||||
|
the ability to display numerical data using various visualizations by using the
|
||||||
|
[D3] library.
|
||||||
|
|
||||||
|
[d3]: https://d3js.org/
|
||||||
|
[2]: https://git.mzhang.io/michael/logseq-calendar
|
||||||
|
|
||||||
|
## Privacy
|
||||||
|
|
||||||
|
Because people are dumping so much of their lives into journals, it's absolutely
|
||||||
|
crucial that boundaries are clear. Without control, this would be a dream come
|
||||||
|
true for any data collection company: rather than having to go out and gather
|
||||||
|
the data, users are entering and structuring it all by themselves.
|
||||||
|
|
||||||
|
End-to-end encryption is a feature that ensures data is never able to be
|
||||||
|
accessed by your storage or synchronization providers. Of course, end-to-end
|
||||||
|
encryption is [not possible unless the entire software is able to be scrutinized
|
||||||
|
by the user or community][1]. Do careful research before deciding who to trust
|
||||||
|
with your data.
|
||||||
|
|
||||||
|
[1]: /posts/2021-10-31-e2e-encryption-useless-without-client-freedom
|
BIN
src/content/posts/2023-08-31-thoughts-on-logseq/logseqQuery.png
Normal file
BIN
src/content/posts/2023-08-31-thoughts-on-logseq/logseqQuery.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 62 KiB |
BIN
src/content/posts/2023-08-31-thoughts-on-logseq/readingList.png
Normal file
BIN
src/content/posts/2023-08-31-thoughts-on-logseq/readingList.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 189 KiB |
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
import PostList from "../components/PostList.astro";
|
import PostList from "../components/PostList.astro";
|
||||||
import BaseLayout from "../layouts/BaseLayout.astro";
|
import BaseLayout from "../layouts/BaseLayout.astro";
|
||||||
import { join } from "path";
|
import { join, dirname } from "path";
|
||||||
|
|
||||||
const currentUrl = Astro.url;
|
const currentUrl = Astro.url;
|
||||||
---
|
---
|
||||||
|
@ -9,5 +9,5 @@ const currentUrl = Astro.url;
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<h2>Blog</h2>
|
<h2>Blog</h2>
|
||||||
|
|
||||||
<PostList basePath={join(currentUrl.pathname, "posts")} drafts="only" />
|
<PostList basePath={join(dirname(currentUrl.pathname), "posts")} drafts="only" />
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
|
@ -3,7 +3,7 @@ import "../../styles/post.scss";
|
||||||
import BaseLayout from "../../layouts/BaseLayout.astro";
|
import BaseLayout from "../../layouts/BaseLayout.astro";
|
||||||
import { type CollectionEntry, getCollection } from "astro:content";
|
import { type CollectionEntry, getCollection } from "astro:content";
|
||||||
import Timestamp from "../../components/Timestamp.astro";
|
import Timestamp from "../../components/Timestamp.astro";
|
||||||
// import BlogPost from "../../layouts/BlogPost.astro";
|
import { getImage } from "astro:assets";
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const posts = await getCollection("posts");
|
const posts = await getCollection("posts");
|
||||||
|
@ -17,17 +17,33 @@ type Props = CollectionEntry<"posts">;
|
||||||
|
|
||||||
const post = Astro.props;
|
const post = Astro.props;
|
||||||
const { Content, remarkPluginFrontmatter } = await post.render();
|
const { Content, remarkPluginFrontmatter } = await post.render();
|
||||||
|
const { heroImage: heroImagePath, heroAlt } = post.data;
|
||||||
|
|
||||||
|
let heroImage;
|
||||||
|
if (heroImagePath) {
|
||||||
|
heroImage = await getImage({ src: heroImagePath });
|
||||||
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<BaseLayout>
|
<BaseLayout>
|
||||||
<h1 class="post-title">{post.data.title}</h1>
|
<div class="post-container">
|
||||||
|
<h1 class="post-title">{post.data.title}</h1>
|
||||||
|
|
||||||
<small class="post-meta">
|
<small class="post-meta">
|
||||||
Posted on <Timestamp timestamp={post.data.date} />
|
Posted on <Timestamp timestamp={post.data.date} />
|
||||||
- {remarkPluginFrontmatter.minutesRead}
|
- {remarkPluginFrontmatter.minutesRead}
|
||||||
</small>
|
</small>
|
||||||
|
|
||||||
<!-- <BlogPost {...post.data}> -->
|
{
|
||||||
<Content />
|
heroImage && heroAlt && (
|
||||||
|
<div style={`background-image: url(${heroImage.src});`} title={heroAlt} class="hero" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
<!-- <BlogPost {...post.data}> -->
|
||||||
|
<div class="post-content">
|
||||||
|
<Content />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<!-- </BlogPost> -->
|
<!-- </BlogPost> -->
|
||||||
</BaseLayout>
|
</BaseLayout>
|
||||||
|
|
|
@ -65,7 +65,7 @@ a code {
|
||||||
}
|
}
|
||||||
|
|
||||||
blockquote {
|
blockquote {
|
||||||
margin-inline: 14px 14px;
|
margin-inline: 0;
|
||||||
color: var(--small-text-color);
|
color: var(--small-text-color);
|
||||||
border-left: 4px solid var(--small-text-color);
|
border-left: 4px solid var(--small-text-color);
|
||||||
padding-left: 12px;
|
padding-left: 12px;
|
||||||
|
|
|
@ -9,6 +9,16 @@
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hero {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 180px;
|
||||||
|
background-size: cover;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 24px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.post-container {
|
.post-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
||||||
|
@ -23,6 +33,11 @@
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@media screen and (max-width: 520px) {
|
@media screen and (max-width: 520px) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -153,7 +168,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
> p {
|
> p {
|
||||||
line-height: 1.5;
|
line-height: 1.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
> p > img {
|
> p > img {
|
||||||
|
@ -163,7 +178,7 @@
|
||||||
|
|
||||||
.footnotes {
|
.footnotes {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
line-height: 1.2;
|
line-height: 1.05;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue