roundabout,
created on Sunday, 2 February 2025, 18:57:35 (1738522655),
received on Sunday, 2 February 2025, 18:57:38 (1738522658)
Author identity: vlad <vlad.muntoiu@gmail.com>
6c88db6a5a118598070852b9873eb4537d9d3d61
.idea/workspace.xml
@@ -4,12 +4,7 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b2c629ea-d173-4caf-b306-cbeaee617270" name="Changes" comment="Add some Izvor screenshots">
<change beforePath="$PROJECT_DIR$/.idea/blog.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/blog.iml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/templates/about.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/about.html" afterDir="false" />
</list>
<list default="true" id="b2c629ea-d173-4caf-b306-cbeaee617270" name="Changes" comment="Add to About" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@@ -27,22 +22,23 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
"keyToString": {
"Python.main.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "master",
"last_opened_file_path": "/home/vlad/blog/static/photos/izvor",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
"vue.rearranger.settings.migration": "true"
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Python.generate.executor": "Run",
"Python.main.executor": "Debug",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"git-widget-placeholder": "master",
"last_opened_file_path": "/home/vlad/blog",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable",
"vue.rearranger.settings.migration": "true"
}
}</component>
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="$PROJECT_DIR$/static/photos/izvor" />
@@ -57,8 +53,8 @@
</key>
</component>
<component name="RunManager">
<configuration name="main" type="PythonConfigurationType" factoryName="Python" nameIsGenerated="true">
<module name="blog" />
<configuration name="generate" type="PythonConfigurationType" factoryName="Python">
<module name="site" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
@@ -66,7 +62,7 @@
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="SDK_NAME" value="Python 3.12 (blog)" />
<option name="SDK_NAME" value="Python 3.10 (blog)" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
@@ -115,6 +111,7 @@
<workItem from="1732718480949" duration="1717000" />
<workItem from="1734782959114" duration="2022000" />
<workItem from="1735979177444" duration="746000" />
<workItem from="1735979929153" duration="2308000" />
</task>
<task id="LOCAL-00001" summary="Blog">
<option name="closed" value="true" />
@@ -356,7 +353,15 @@
<option name="project" value="LOCAL" />
<updated>1734783230169</updated>
</task>
<option name="localTasksCounter" value="31" />
<task id="LOCAL-00031" summary="Add to About">
<option name="closed" value="true" />
<created>1735982089613</created>
<option name="number" value="00031" />
<option name="presentableId" value="LOCAL-00031" />
<option name="project" value="LOCAL" />
<updated>1735982089613</updated>
</task>
<option name="localTasksCounter" value="32" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -374,7 +379,6 @@
</option>
</component>
<component name="VcsManagerConfiguration">
<MESSAGE value="Add an article on Ampoule" />
<MESSAGE value="Extra bullet point" />
<MESSAGE value="Remove None" />
<MESSAGE value="Update article" />
@@ -399,9 +403,11 @@
<MESSAGE value="Add date to all posts" />
<MESSAGE value="More articles" />
<MESSAGE value="Add some Izvor screenshots" />
<option name="LAST_COMMIT_MESSAGE" value="Add some Izvor screenshots" />
<MESSAGE value="Add to About" />
<option name="LAST_COMMIT_MESSAGE" value="Add to About" />
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/blog$generate.coverage" NAME="generate Coverage Results" MODIFIED="1735982188194" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
<SUITE FILE_PATH="coverage/blog$main.coverage" NAME="main Coverage Results" MODIFIED="1734783549604" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
</component>
</project>
articles/Beginner distros.md
articles/Browsers are doing too much.md
articles/GNU-Linux not Linux.md
main.py
@@ -55,6 +55,9 @@ for project in projects:
topics = sorted(topics.items(), key=lambda x: x[0])
index_page = ampoule.Page(site, "index.html", topics=topics)
for topic, posts in topics:
site.add_page(f"/index/{topic}.html", ampoule.Page(site, "index-tag.html", topic=topic, posts=posts))
# Add the pages to the site
site.add_page("/", main_page)
site.add_page("/projects/", projects_page)
projects/izvor.md
@@ -0,0 +1,42 @@
---
title: Izvor
source-url: https://roundabout-host.com/roundabout/izvor
topics: ["gtk", "software", "python", "gnu/linux", "gpl", "izvor", "utility"]
---
Izvor is a GNU/Linux universal search/launcher utility.

It is written in Python, using GTK (3, not 4, and without libadwaita) for the
graphical interface. It is licensed under the GPL.
It ships without any search sources. The default search engines are located at
[Izvor Modules](https://roundabout-host.com/roundabout/izvor-modules/).
Features
--------
* Search anything on your device and more: applications, files, run commands
and more
* Fully modular (every search engine is a separate Python module), engines are
easy to write
* Asynchronous; results are delivered when they become available
* Native look and feel using GTK+, no stupid WWW GUI or hacker styling like half
of the launchers out there; of course, it respects your GTK theme
* User modules are as easy to access as standard ones; no need to prefix your
queries
* Fully keyboard-operable, but still usable with mouse; choose your preferred
input method
* Plugins are configured using JSON
* Libre (GPLv3)
Currently, it is in development, lacks fuzzy search and localisation (but the main
window has no text) and is not yet packaged for any distribution. It is, however,
prepared for Flatpak, and I use it myself.
Meaning of the name
-------------------
"Izvor" is a Serbo-Croatian, Romanian, Bulgarian and Macedonian word meaning
"source". This reflects the launcher's ability to bring you search results from
many sources.
It is pronounced as "Eez-vor" with the accent as in "Izvōr", not "Īzvor".
projects/roundabout.md
@@ -23,24 +23,26 @@ stage, it supports:
* Themeing
* Static site hosting
* Really basic search
* Markdown, but only for files (see below)
* Markdown formatting for comments, Markdown file rendering
* Comments on commit diffs
* Partial XML API, will be expanded
* Decentralised merging
* Forum labels
* Static site hosting
* Organisations by trusting users to take actions on your behalf
For now, it doesn't support, but certainly **will** before the 1.0.0 release:
* Code review features (like comments on commits, for now you'll use the forum), something
like Google Docs comments is planned
For now, it doesn't support, but I won't consider it complete without:
* Decentralised social features (merging is already decentralised)
* CI/CD
* API
* Repository metadata (licence, topics, language, etc.)
* Decentralization
* Packaging
* Package distribution
* Code syntax highlighting
* Any kind of statistics
* Web-based code editing
* Users changing passwords
* Admin panel
* Forum labels
* User mentions
* Markdown comments (we just need to cache the HTML so it won't be too slow)
* Git over SSH
It won't support:
* Wikis (why have another interface just for markdown, when you can use your own repo and publish
@@ -49,11 +51,8 @@ It won't support:
I'm still not sure about:
* Project boards (what about making label grouping instead?)
* Advertising (don't worry, instances could disable it, and logged-in users could also disable it
for free, and it won't track anyone, no video, animation, sound, popups, overlays, or JS)
* Advertising (I want a truly JS-free, libre advertising network)
* Paste service (does it really work well with Git? Couldn't you just use a repo for that?)
* Git over SSH or other protocols (do people need it? HTTPS does the same things, and it's easier
to set up; doesn't require daemons)
* Native clients (Android development is hard, iOS is hostile, and on desktop it's not needed,
there are GUI git clients that work with any server, for social features web is fine)
* IDE plugins (sounds good, but it's hard work)
static/style.css
@@ -258,11 +258,12 @@ pre {
border: none;
}
.project-title > span {
.project-title > h1 {
padding: 0.5rem;
border: none;
}
.project-title > a:link, .project-title > a:visited {
.project-title > a:any-link {
text-decoration: none;
background: #009688;
color: #ffffff;
@@ -270,13 +271,14 @@ pre {
border: 4px solid #00796B;
box-sizing: border-box;
line-height: 1.25em;
font-size: 0.625em;
font-size: 1.5em;
font-family: "Romanian League Gothic", sans-serif;
text-transform: uppercase;
display: flex;
align-items: center;
justify-content: center;
min-width: 25%;
height: 2lh;
transition: 400ms;
}
@@ -344,7 +346,7 @@ img:not(.article-image) {
height: auto;
}
.topic-expander .topic-posts {
.topic-posts {
display: flex;
flex-direction: column;
gap: 0.5rem;
@@ -396,3 +398,19 @@ img:not(.article-image) {
top: -1rem;
left: -1rem;
}
.tags {
margin: 0;
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
font-family: "Source Sans", sans-serif;
}
.tag::before {
content: "#";
}
.tag {
text-decoration: none;
}
templates/article.html
@@ -4,7 +4,12 @@
{% endblock %}
{% block content %}
<h1>{{ document["title"] }}</h1>
<div id="article-date">{{ document.date | strftime("%Y-%m-%d, %H:%M:%S") }}</div>
<div id="article-date">{{ document.date | strftime("%Y-%m-%d") }}</div>
<p class="tags">
{% for tag in document["topics"] %}
<a href="/index/{{ tag }}.html" class="tag">{{ tag }}</a>
{% endfor %}
</p>
{% if document["image"] %}
<figure>
<img src="{{ document['image'] }}" alt="{{ document['image-alt'] }}" id="article-image-header">
templates/index-tag.html
@@ -0,0 +1,15 @@
{% extends "default.html" %}
{% block title %}{{ topic }}{% endblock %}
{% block content %}
<h1>{{ topic }}</h1>
<div class="topic-posts">
{% for type, post in posts %}
<article class="content-area">
<h2><a href="/{{ type }}/{{ post.file_name }}" class="article-title">{{ post["title"] }}</a></h2>
<div class="home-article-date">{{ post.date | strftime("%Y-%m-%d") }}</div>
<p>{{ post.content | first_paragraph | markdown }}</p>
</article>
{% endfor %}
</div>
{% endblock %}
templates/project.html
@@ -3,10 +3,17 @@
{{ document["title"] }}
{% endblock %}
{% block content %}
<h1 class="project-title">
<span>{{ document["title"] }}</span>
<a href="{{ document['source-url'] }}">Repository</a>
</h1>
<div class="project-title">
<h1>
{{ document["title"] }}
</h1>
<a href="{{ document['source-url'] }}" class="repository-button">Go to repository</a>
</div>
<p class="tags">
{% for tag in document["topics"] %}
<a href="/index/{{ tag }}.html" class="tag">{{ tag }}</a>
{% endfor %}
</p>
<article class="content-area">
{{ document.content | markdown }}
</article>