first commit
This commit is contained in:
commit
7fba4dbbd6
4
examples.md
Normal file
4
examples.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Examples of things to do with Tickle
|
||||
|
||||
1. A documentation site. This is one! The repo of this site is an example of Tickle being used for docs.
|
||||
2. A blog. TODO: add a link
|
258
index.html
Normal file
258
index.html
Normal file
@ -0,0 +1,258 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tickle</title>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Lobster&family=Nunito+Sans:ital,wght@0,400;0,700;1,400&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<style>
|
||||
:root {
|
||||
--accent: hotpink;
|
||||
--background: #fdfdfd;
|
||||
font-family: "Nunito Sans", sans-serif;
|
||||
}
|
||||
body,
|
||||
html {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.trigger {
|
||||
display: none;
|
||||
}
|
||||
.left-nav > * {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.left-nav > .menu {
|
||||
padding: 1em;
|
||||
transform: translateX(-100%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
transition: all 0.3s ease-out;
|
||||
gap: 1em;
|
||||
}
|
||||
.left-nav > .menu a {
|
||||
text-decoration: none;
|
||||
background: var(--background);
|
||||
color: var(--accent);
|
||||
box-shadow: rgba(100, 100, 111, 0.2) 0px 7px 29px 0px;
|
||||
padding: 0.2em 0.4em;
|
||||
}
|
||||
.left-nav > .menu a:hover {
|
||||
background: var(--accent);
|
||||
color: var(--background);
|
||||
}
|
||||
.left-nav > .trigger:checked ~ .menu {
|
||||
transform: translateX(0);
|
||||
transition-duration: 0.1s;
|
||||
}
|
||||
.burger {
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-content: center;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 2em;
|
||||
border-radius: 0.2em;
|
||||
box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
|
||||
transition: background 0.5s ease;
|
||||
}
|
||||
.left-nav > .trigger:checked ~ .burger {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.left-nav > .trigger:checked ~ .burger,
|
||||
#Loading {
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 0;
|
||||
background: rgba(17, 17, 17, 0.2);
|
||||
}
|
||||
#Loading {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 8rem;
|
||||
color: var(--accent);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease-in, height 0s linear 0.3s;
|
||||
height: 0;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
.is-loading #Loading {
|
||||
opacity: 1;
|
||||
height: 100%;
|
||||
transition: opacity 1s ease-in, height 0 linear;
|
||||
}
|
||||
#Loading::after {
|
||||
content: "❤";
|
||||
animation: beat 1.2s infinite cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||
}
|
||||
@keyframes beat {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
5% {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
39% {
|
||||
transform: scale(0.85);
|
||||
}
|
||||
45% {
|
||||
transform: scale(1);
|
||||
}
|
||||
60% {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
100% {
|
||||
transform: scale(0.9);
|
||||
}
|
||||
}
|
||||
#Body {
|
||||
padding: 3em;
|
||||
max-width: 52em;
|
||||
margin: 0 auto;
|
||||
}
|
||||
#Body h1,
|
||||
#Body h2,
|
||||
#Body h3,
|
||||
#Body h4 {
|
||||
color: var(--accent);
|
||||
font-family: "Lobster", serif;
|
||||
}
|
||||
#Body a {
|
||||
color: var(--background);
|
||||
background-color: var(--accent);
|
||||
text-decoration: none;
|
||||
padding: 0.1em 0.4em;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="left-nav">
|
||||
<input id="main-nav" type="checkbox" class="trigger" />
|
||||
<label for="main-nav" class="burger">≡</label>
|
||||
<div id="Menu" class="menu"></div>
|
||||
</nav>
|
||||
<header id="Menu"></header>
|
||||
<main id="Body"></main>
|
||||
<div id="Loading"></div>
|
||||
<script type="module">
|
||||
/**
|
||||
* markdown parser. Remove if you don't use markdown
|
||||
*/
|
||||
// @ts-ignore
|
||||
import { micromark } from "https://esm.sh/micromark@3?bundle";
|
||||
|
||||
/**
|
||||
* useful to check for transitions while developing styles, if the loading screen disappears too fast
|
||||
*
|
||||
*/
|
||||
const wait = (val) => new Promise((ok) => setTimeout(ok, 1, val));
|
||||
|
||||
/**
|
||||
* The elements we will need
|
||||
*/
|
||||
const [Menu, Body, Loading] = ["Menu", "Body", "Loading"].map((id) =>
|
||||
document.getElementById(id)
|
||||
);
|
||||
|
||||
/**
|
||||
* cache the original title to append it to the page title
|
||||
*/
|
||||
const mainTitle = document.title;
|
||||
|
||||
const startLoading = () => document.body.classList.add("is-loading");
|
||||
const stopLoading = () => document.body.classList.remove("is-loading");
|
||||
|
||||
const getCurrentPage = () => {
|
||||
const [path, searchStr] = (
|
||||
window.location.hash[1] === "/" ? window.location.hash.slice(2) : ""
|
||||
).split("?");
|
||||
const params = new URLSearchParams(searchStr);
|
||||
return { path, params };
|
||||
};
|
||||
|
||||
const onHashChange = (evt) => {
|
||||
const { path, params } = getCurrentPage();
|
||||
if (!path) {
|
||||
return false;
|
||||
}
|
||||
startLoading();
|
||||
return fetch(`./${path}`)
|
||||
.then((response) => response.text())
|
||||
.then(wait)
|
||||
.then((text) => {
|
||||
const [, title] = text.match(/^(#\s\w+)/) ||
|
||||
text.match(/(.*?)\n===+/m) || [, path];
|
||||
document.title = `${title} | ${mainTitle}`;
|
||||
if (params.has("source")) {
|
||||
Body.innerHTML = `<pre>${text}</pre>`;
|
||||
} else {
|
||||
Body.innerHTML = micromark(text);
|
||||
}
|
||||
stopLoading();
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener("hashchange", onHashChange);
|
||||
|
||||
/**
|
||||
* Loads the article list, parses it, creates the menu items
|
||||
*/
|
||||
const start = () => {
|
||||
startLoading();
|
||||
fetch("./files.txt")
|
||||
.then((response) => response.text())
|
||||
.then((lines) => {
|
||||
Menu.innerHTML = lines
|
||||
.split(`\n`)
|
||||
.map((line, index) => {
|
||||
const today = new Date().toISOString().split("T")[0];
|
||||
const result = line.match(
|
||||
/(?<name>.+)\.(?<ext>\w{2,3})(?:\s(?<date>[\d-]+)?(?<title>.+))?/
|
||||
);
|
||||
if (!result) {
|
||||
console.log(`could not parse line ${index}: [${line}]`);
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
groups: { name, ext, date = today, title = name },
|
||||
} = result;
|
||||
const href = `/${name}.${ext}`;
|
||||
if (!getCurrentPage().path) {
|
||||
window.location.hash = `#${href}`;
|
||||
}
|
||||
console.log({ name, href, date, title });
|
||||
return { name, href, date, title };
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort(({ date: a }, { date: b }) => a - b)
|
||||
.map(
|
||||
({ href, title }) => `<a data-link href="#${href}">${title}</a>`
|
||||
)
|
||||
.join(`\n`);
|
||||
|
||||
onHashChange();
|
||||
});
|
||||
};
|
||||
|
||||
start();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
68
readme.md
Normal file
68
readme.md
Normal file
@ -0,0 +1,68 @@
|
||||
# Tickle
|
||||
|
||||
Possibly the simplest way to self-publish on the web today.
|
||||
|
||||
Tickle is a self-publishing platform with **no build system**, and **no tooling required**.
|
||||
|
||||
|
||||
## What is Tickle?
|
||||
|
||||
A small, small, small script that will load markdown files, parse them in the browser, and display them.
|
||||
|
||||
Accent is kept on keeping the script aired, readable, and easy to hack.
|
||||
|
||||
By default, Tickle will:
|
||||
|
||||
1. Read a text file containing a list of posts
|
||||
2. Load the first post
|
||||
3. Parse the markdown
|
||||
4. Display the list of links, and the pages
|
||||
|
||||
Tickle is small enough that a moderately knowledgeable programmer should be able to make it do whatever they want.
|
||||
|
||||
|
||||
## How do I use Tickle?
|
||||
|
||||
1. write markdown files
|
||||
2. reference your files in a text file called "files.txt"
|
||||
3. upload everything on some server
|
||||
|
||||
We have a [neat little UI to help you do all of that](https://git.poto.cafe/yagich/ticle-godot-frontend). You don't have to use it, but it's neat.
|
||||
|
||||
Here are more detailed explanations, for different knowledge levels.
|
||||
|
||||
- [I do not care about tech stuff, I just want to publish](/#/tutorial-publishers.md)
|
||||
- [I can write some code, give me detailed explanations](/#/tutorial-hackers.md)
|
||||
|
||||
It's important to know that Tickle is an *idea*. Think of it as a specification. We have a few examples listed [here](examples.md)
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q: Why would I want to use this rather than medium/similar?
|
||||
|
||||
**A**: Medium locks your content behind paywalls and spies on users. Other providers have similar issues. If you care about being in control of your data, or not having to deal with embargos, or not spying on people, or not helping corporations, or keeping your tools simple and lightweight, then you'll probably like Tickle. If you don't care, then using something like Medium probably fits your needs better.
|
||||
|
||||
### Q: What if I want custom functionality, but don't know programming at all?
|
||||
|
||||
**A**: Check the [examples](examples.md) first, and see if there's something for you. Otherwise, write to us! Find us on X or Y, and shoot your request. Maybe we'll get to it
|
||||
|
||||
### Q: Everything in the browser? Isn't that bad for SEO?
|
||||
|
||||
**A**: Who cares? SEO is dead. No one finds blogs organically by googling them, the first pages are entirely occupied by clickbait. If your articles will be shared, they will be shared by people, and the technical side of your page counts zilch there
|
||||
|
||||
### Q: Isn't it bad for performance?
|
||||
|
||||
**A**: Again, who cares? For Facebook, a microsecond lost means a large amount of people will not see ads fast enough, that loses them money. But someone who comes to read your blog will wait 300ms to read your article, no worries.
|
||||
|
||||
### Q: What about accessibility?
|
||||
|
||||
**A**: We haven't started working on this, but we care about this a lot, and we will make sure Tickle websites are accessible, at least in the default blog recipe.
|
||||
|
||||
### Q: What's the license?
|
||||
|
||||
**A**: The license is [[upcoming]]
|
||||
|
||||
### Q: "Tickle"?
|
||||
|
||||
**A**: It stands for "article"... Or maybe "art", "tickle"? It's a neat name found by Otto.
|
9
tutorial-hackers.md
Normal file
9
tutorial-hackers.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Tutorial & Info for peeps who can code a bit
|
||||
|
||||
Tickle consists of 3 distinct and simple parts:
|
||||
|
||||
1. load text files
|
||||
2. parses markdown files in the browser
|
||||
3. displays the markdown files
|
||||
|
||||
To parse markdown files, the default implementation uses Micromark, but feel free to plug any parser you like.
|
44
tutorial-publishers.md
Normal file
44
tutorial-publishers.md
Normal file
@ -0,0 +1,44 @@
|
||||
# I want to use Tickle, but I don't want to do anything technical
|
||||
|
||||
Unfortunately, we couldn't remove entirely all the technicalities. But we've managed to keep them fairly low.
|
||||
|
||||
There are 4 steps to writing articles in Tickle:
|
||||
|
||||
1. Write the articles in Markdown
|
||||
2. List the articles in a file
|
||||
3. Optionally, preview the site locally before uploading
|
||||
4. Upload everything on a server
|
||||
|
||||
We have a desktop app that takes care of steps 1, 2, and 3 (and maybe some day step 4!). It's comfy, made with love, and you can install it from https://git.poto.cafe/yagich/ticle-godot-frontend
|
||||
|
||||
But if you want to do the things manually, read on. You need to do step 4 manually either way.
|
||||
|
||||
## 1. Write A Few Articles
|
||||
|
||||
You will need some knowledge of Markdown. This is how we write the articles. It's really not hard! In fact, it looks very similar to how you already use characters in chat for emphasis. Check [a reference here](https://commonmark.org/help/). Create a document, call it "my-first-article.md" for example (`.md` stands for "markdown"). Write some text in it.
|
||||
|
||||
## 2. Create the List of Files
|
||||
|
||||
Once you have written a document, you then open a new file, call it "files.txt". At the top, write:
|
||||
|
||||
```
|
||||
my-first-article.md 2022-03-03 My First Article
|
||||
```
|
||||
|
||||
After saving the file, you're done! You can write more articles, and reference them in the same text file, one per line.
|
||||
|
||||
At this state, the site is "ready". You can upload it, but before that, you probably want to preview it.
|
||||
|
||||
## 3. Preview the site
|
||||
|
||||
You will need to run a server. The easier server to run is [our own](https://git.poto.cafe/yagich/ticle-godot-frontend), but if you want other options, you want to search for "easy http server", and see what you get.
|
||||
|
||||
- TODO: list a few good options here
|
||||
|
||||
Once you have the server up and running, you can open a browser to the address the site opened. Usually, something like http://localhost:3000. The number is arbitrary and might be anything; it depends on the server.
|
||||
|
||||
Once you're satisfied, time to upload
|
||||
|
||||
## 4. Upload the site
|
||||
|
||||
TODO: write procedure
|
Loading…
Reference in New Issue
Block a user