Compare commits

...

43 Commits

Author SHA1 Message Date
Soren I. Bjornstad
295f61299a update sphinx version 2023-10-15 22:11:12 -05:00
Soren I. Bjornstad
18564356c3 add RTD requirements.txt 2023-10-15 22:06:31 -05:00
Soren I. Bjornstad
02786a1fb1 fix type error in sphinx config 2023-10-15 22:03:02 -05:00
Soren I. Bjornstad
d5445f7a1a add RTD config file (now required) 2023-10-15 22:01:18 -05:00
Soren I. Bjornstad
77d95a5ef8 bump version 2023-10-15 21:56:14 -05:00
Soren I. Bjornstad
9b0b5a1824 fix 'npm bin' bug and update to latest ZK 2023-10-15 21:55:26 -05:00
Soren I. Bjornstad
553c175eb5 remove uses of "npm bin"
This was removed from recent Node versions. Using 'npx' is the new way
and should resolve this issue.

I suppose we might run into someone who had a non-working npx and it
breaks, but we'll cross that bridge when we come to it; it works in my
testing and there are people broken in the current version.
2023-10-15 21:27:22 -05:00
Soren I. Bjornstad
650b21a03d fix docs copyright date 2022-08-19 17:36:01 -05:00
Soren I. Bjornstad
96f149feaa use API token to upload to PyPi
User/password auth is deprecated for Twine.
2022-08-19 17:32:38 -05:00
Soren I. Bjornstad
a1c946db9e update to latest ZK version, bump version number 2022-08-19 17:27:43 -05:00
Soren I. Bjornstad
04272c3973 also privatize JSON tiddlers
This seems slightly dangerous because we're just applying the same
replacement logic and JSON has a much fussier syntax. However, if any
tiddlers end up invalid we should just get a build failure, so this
doesn't seem highly consequential.
2022-08-19 17:14:06 -05:00
Soren I. Bjornstad
1f56f68448 stop using <<privateperson>> macro
Since we have to do raw links anyway in many cases, it doesn't really
make sense to use two different formats. This also gets around an edge
case where replacement produces invalid syntax if the person to be
replaced is linked within a macro, since the >> of privateperson then
terminates the outside macro -- given my increasing use of footnotes,
this isn't all that uncommon.
2022-08-19 17:09:20 -05:00
Soren I. Bjornstad
3e9ecd4b70 work around ReadTheDocs bug 2022-03-27 15:22:31 -05:00
Soren I. Bjornstad
7be018ea3f pull ZK updates and bump version 2022-03-27 15:19:41 -05:00
Soren I. Bjornstad
8fb496bbfe Merge branch 'new-private-person-replacement-logic' into march-tweaks 2022-03-27 15:14:41 -05:00
dcac80c5ac Replace link text of private person
It is possible that a private person is linked to in a tiddler with the
syntax `[[Jane|MsJaneDoe]]`.

Up to now, the text of the link was not redacted, which could lead to
unintentional privacy leaks.

Therefore, a new parameter `replace_link_text` is introduced for the
`replace_private_people` builder.

When that is set to True, then the link text is also replaced with the
initials.

Signed-off-by: Jacob Kiers <code@kiers.eu>
2022-03-22 23:34:32 +01:00
Soren I. Bjornstad
4875f7c1f5 avoid incrementing iterator when not appropriate
By chance, this hasn't caused any problems yet.
2022-03-16 12:33:20 -05:00
Soren I. Bjornstad
f0bd41f65a implement more robust private-person replacer
The previous method occasionally gave incorrect results because it
performed replacements on an entire line, rather than on individual
instances of the text to replace. This usually worked fine, but in rare
cases could create wrong/ugly output, and with future improvements to
what can be replaced could end up causing leaks.

This was a royal PITA to get working, but I'm fairly sure it's correct
now due to all the doctests. Please add more if you find any regressions
or think of cases that aren't covered.
2022-03-16 09:19:49 -05:00
Soren Bjornstad
d3f6216837
Merge pull request #4 from jacobkiers/allow-specifying-listening-host
Allow setting the host parameter
2022-03-14 21:01:35 -05:00
Soren I. Bjornstad
e14709be40 update copyright notice 2022-03-14 20:59:57 -05:00
bdeaac5d03 Specify listen_host as an option in the config file
Signed-off-by: Jacob Kiers <code@kiers.eu>
2022-03-14 18:22:07 +01:00
Soren I. Bjornstad
4acff2731a remove debug print mistakenly left in 2022-03-11 20:14:30 -06:00
3b1f91bcb0 Allow setting the host parameter
In order to run in a Docker container, it is necessary to listen on all interfaces (`0.0.0.0`).
So make it possible to set the host.

Fixes #1

Signed-off-by: Jacob Kiers <code@kiers.eu>
2022-03-12 02:31:57 +01:00
Soren I. Bjornstad
572c3d0316 bump version and allow use of --version to see it 2022-01-23 17:28:19 -06:00
Soren I. Bjornstad
4a6e4e7bc0 don't crash if tzk is used without arguments 2022-01-23 17:22:45 -06:00
Soren I. Bjornstad
35ca2b896e regular update 2022-01-03 15:31:14 -06:00
Soren I. Bjornstad
0705c55e77 fix only one PrivatePerson replacement working per line
Because the 'line' variable was already bound in the outer loop, changes
to the line in the inner loop were lost in subsequent inner-loop
iterations, if required. Change to always indexing the array (which is
defined outside the scope of the loops) directly on every reference.

(This is why it's usually best not to modify things while looping over
them. Maybe a refactor could be in order.)
2021-11-29 21:47:21 -06:00
Soren I. Bjornstad
f8edb70065 bump version 2021-09-22 11:34:02 -05:00
Soren I. Bjornstad
218705bdc9 add walkthrough video link 2021-09-22 11:33:19 -05:00
Soren I. Bjornstad
48a8789c4d activate venv before trying publish
Was planning to do this myself, but I forgot so many times I'm just
adding it...
2021-09-22 11:29:05 -05:00
Soren Bjornstad
48ab7f9514
add walkthrough video to README 2021-09-22 11:28:36 -05:00
Soren I. Bjornstad
f2d9a2abe6 bump version 2021-09-22 10:58:25 -05:00
Soren I. Bjornstad
cb55a967d0 remove build folder prior to building wheels
Otherwise files that have been removed will still show up in the wheel
-- but not in the source distribution -- which is super confusing and
wrong. No idea why Python doesn't check this itself.
2021-09-22 10:57:18 -05:00
Soren I. Bjornstad
52c0daedbb fix tray 2021-09-22 10:54:06 -05:00
Soren I. Bjornstad
09b856488b bump version 2021-09-22 08:06:32 -05:00
Soren I. Bjornstad
9218a83d41 fix caption/description on several buttons 2021-09-22 07:58:17 -05:00
Soren I. Bjornstad
a896244367 fix permalink button going to my Zettelkasten 2021-09-22 07:52:15 -05:00
Soren I. Bjornstad
ec83de9978 fix 'compile_html_file' to use dirs_exist_ok
5065efd3 incorrectly changed to doing an rmtree of the entire target
since that was what editionify needed. Editionify should clean the
target directory, but the single HTML file build should not (it ended up
wiping my git repository from my hard drive). We might wish to revisit
this particular fix in the future since it still fails to delete removed
extimages, but this doesn't seem like a big deal.
2021-09-21 21:13:30 -05:00
Soren I. Bjornstad
9a70b67af7 bump version 2021-09-21 20:45:44 -05:00
Soren I. Bjornstad
86441e9618 don't carry FileSystemPaths into emptified edition
Better for people to set up their own scheme if they want one, and it
avoids tiddlers potentially being in two different places (since
FileSystemPaths isn't honored during an init/savewikifolder).
2021-09-21 20:44:12 -05:00
Soren I. Bjornstad
2f53a8f616 fix missing link and missing kill phrases tiddler 2021-09-21 20:20:37 -05:00
Soren I. Bjornstad
c335597b57 adjust explanation of restore-plugins situation
It was just me being dense...I was overwriting the existing plugins
array.
2021-09-21 13:08:55 -05:00
Soren I. Bjornstad
a7bdd2397e fix bad URL to license 2021-09-21 12:44:53 -05:00
1157 changed files with 8610 additions and 21043 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ venv/
cli_docs/_build/
dist/
build/
.pypi_token

35
.readthedocs.yaml Normal file
View File

@ -0,0 +1,35 @@
# Read the Docs configuration file for Sphinx projects
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the OS, Python version and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.11"
# You can also specify other tool versions:
# nodejs: "20"
# rust: "1.70"
# golang: "1.20"
# Build documentation in the "docs/" directory with Sphinx
sphinx:
configuration: cli_docs/conf.py
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
# builder: "dirhtml"
# Fail on all warnings to avoid broken references
# fail_on_warning: true
# Optionally build your docs in additional formats such as PDF and ePub
# formats:
# - pdf
# - epub
# Optional but recommended, declare the Python requirements required
# to build your documentation
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
python:
install:
- requirements: cli_docs/requirements.txt

View File

@ -1,4 +1,4 @@
Copyright © 2021 Soren Bjornstad.
Copyright © 2021-2022 Soren Bjornstad and the tzk community.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

View File

@ -19,7 +19,7 @@ Some basic knowledge of [TiddlyWiki](https://tiddlywiki.com)
Documentation / Installation
============================
----------------------------
Find a live version of the edition on the web with further instructions
at https://sobjornstad.github.io/tzk/.
@ -28,3 +28,14 @@ You can find the documentation for the CLI
versioning with Git,
and creating public and other derivative versions of the wiki)
at https://tzk.readthedocs.io/en/latest/.
Video walkthrough
-----------------
Since tzk is still in an alpha state,
general instructions on how to use it are somewhat lacking at the moment.
To fill in the gap in the meantime,
I recorded an hour-long walkthrough showing how you might take notes using tzk;
you can select individual sections to watch if that's longer than you want.
[Watch the video on YouTube](https://www.youtube.com/watch?v=9X_T-DVie8o).

View File

@ -5,7 +5,7 @@
2. Update version numbers:
`setup.py`,
`cli_docs/conf.py`,
`util.py`.
`tzk/util.py`.
3. Push changes to GitHub.
Check that RTD and GitHub Pages update as desired
(https://tzk.readthedocs.io/en/latest/,

View File

@ -6,28 +6,25 @@
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# Instead of adding things manually to the path, we just ensure we install esc
# in editable mode in our environment.
import datetime
# -- Project information -----------------------------------------------------
project = 'tzk'
copyright = '2021 Soren Bjornstad'
copyright = str(datetime.date.today().year) + ' Soren Bjornstad'
author = 'Soren Bjornstad'
# The short X.Y version
version = "0.1.0"
version = "0.4.0"
# The full version, including alpha/beta/rc tags
release = "0.1.0"
release = "0.4.0"
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
needs_sphinx = '1.8.5'
needs_sphinx = '4.2.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
@ -52,13 +49,6 @@ source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.

View File

@ -0,0 +1,4 @@
# docs
sphinx==4.2.0 # newer versions display function names in the wrong color?
sphinx_rtd_theme==0.5.2
Jinja2<3.1 # https://github.com/readthedocs/readthedocs.org/issues/9038

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,9 @@
#!/bin/bash
set -e
. venv/bin/activate
rm -rf build/
rm -f dist/*
mkdir -p dist
python setup.py sdist bdist_wheel
twine upload dist/*
TWINE_PASSWORD=$(cat .pypi_token) twine upload --username '__token__' dist/*

View File

@ -2,8 +2,9 @@
-e .
# docs
sphinx==3.5.4 # newer versions display function names in the wrong color?
sphinx==4.2.0 # newer versions display function names in the wrong color?
sphinx_rtd_theme==0.5.2
Jinja2<3.1 # https://github.com/readthedocs/readthedocs.org/issues/9038
# dev tools
build

View File

@ -9,7 +9,7 @@ with open("README.md", "r") as fh:
setuptools.setup(
name="tzk",
version="0.1.0",
version="0.4.0",
author="Soren I. Bjornstad",
author_email="zettelkasten@sorenbjornstad.com",
description="Build tool for TiddlyWiki Zettelkasten",

View File

@ -13,6 +13,8 @@ from tzk import tw
from tzk.util import (BuildError, fail, numerize, require_dependencies, pushd,
TZK_VERSION)
VERSION_INFO = f"tzk version {TZK_VERSION}"
class CliCommand(ABC):
"""
@ -83,6 +85,12 @@ class ListenCommand(CliCommand):
@classmethod
def setup_arguments(cls, parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"--host",
metavar="HOST",
help="Host to listen on.",
default=str(cm().listen_host or "127.0.0.1"),
)
parser.add_argument(
"-p", "--port",
metavar="PORT",
@ -109,6 +117,7 @@ class ListenCommand(CliCommand):
tw.exec(
[
("listen",
f"host={args.host}",
f"port={args.port}",
f"username={args.username}",
f"password={args.password}")
@ -180,7 +189,7 @@ class VersionCommand(CliCommand):
pass
def execute(self, args: argparse.Namespace) -> None:
print(f"tzk version {TZK_VERSION}")
print(VERSION_INFO)
class BuildCommand(CliCommand):
@ -385,6 +394,7 @@ def launch():
# go there before doing anything else.
if (not os.path.exists("tzk_config.py")
and os.environ.get('TZK_DIRECTORY')
and len(sys.argv) > 1
and sys.argv[1] != "init"): # we can't init an existing TZK_DIRECTORY
try:
os.chdir(os.environ['TZK_DIRECTORY'])
@ -401,11 +411,17 @@ def launch():
parser = argparse.ArgumentParser(
description=f"TiddlyZettelKasten {TZK_VERSION} CLI\n"
f"Copyright (c) 2021 Soren Bjornstad.\n"
f"MIT license; see https://github.com/sobjornstad/tzk/LICENSE for details.",
f"Copyright (c) 2021-2022 Soren Bjornstad and the tzk community.\n"
f"MIT license; see https://github.com/sobjornstad/tzk/blob/master/LICENSE for details.",
epilog="For full documentation, see https://tzk.readthedocs.io/en/latest/.",
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
"--version",
action="version",
version=VERSION_INFO,
help=VersionCommand.help
)
subparsers = parser.add_subparsers()
for command in sorted(CliCommand.__subclasses__(), key=lambda i: i.__name__):

View File

@ -13,13 +13,14 @@ information about the defined products without actually running any build steps.
from collections.abc import Mapping
from contextlib import contextmanager
import functools
import itertools
import os
from pathlib import Path
import re
import shutil
import subprocess
import tempfile
from typing import Callable, Dict, List, Optional, Set, Sequence, Tuple
from typing import Callable, Dict, Generator, List, Optional, Set, Sequence, Tuple
from tzk import git
from tzk import tw
@ -188,13 +189,13 @@ def check_for_kill_phrases(kill_phrase_file: str = None) -> None:
:param kill_phrase_file: The path from the source wiki's root directory to the
config tiddler containing kill phrases. In the default
Zettelkasten edition, this is
tiddlers/_system/config/zettelkasten/Build/KillPhrases.tid;
"tiddlers/$__config_zettelkasten_Build_KillPhrases.tid";
if you change the way paths are determined, you can give
a different path here.
"""
assert 'public_wiki_folder' in build_state, "new_output_folder builder must run first"
if kill_phrase_file is None:
kill_phrase_file = "tiddlers/_system/config/zettelkasten/Build/KillPhrases.tid"
kill_phrase_file = "tiddlers/$__config_zettelkasten_Build_KillPhrases.tid"
kill_phrases = set()
with open(kill_phrase_file) as f:
@ -278,7 +279,8 @@ def compile_html_file(
will be placed, relative to the private wiki's root directory.
Default ``output/public_site``.
:param overwrite: If the ``output_folder`` already exists,
should we overwrite it? Default True.
should we overwrite any files in it with the same name?
Default True.
:param externalize_attachments: If True, update tiddlers that match
the ``attachment_filter`` parameter to point to
external versions of their content.
@ -311,13 +313,10 @@ def compile_html_file(
f"(To overwrite any files existing in the output folder, "
f"set overwrite = True for this builder.)")
try:
shutil.rmtree(output_folder)
except FileNotFoundError:
pass
shutil.copytree(
Path(build_state['public_wiki_folder']) / "output",
Path(output_folder),
dirs_exist_ok=True
)
info(f"Successfully copied built output to {os.path.abspath(output_folder)}.")
@ -361,8 +360,165 @@ def _private_people_replacement_table(
}
def _privatize_line(line: str, replacement_table: Dict[str, str],
replace_link_text: bool = False) -> Optional[str]:
"""
Given a line and a table of replacements to make, replace all instances
of all private people defined in the replacement table.
Basics:
>>> _privatize_line("MsAlice is a test person.", {'MsAlice': 'A.'})
'[[A.|PrivatePerson]] is a test person.'
>>> _privatize_line("This woman, known as MsAlice, is a test person.", \
{'MsAlice': 'A.'})
'This woman, known as [[A.|PrivatePerson]], is a test person.'
>>> _privatize_line("[[MsAlice]] is a test person.", {'MsAlice': 'A.'})
'[[A.|PrivatePerson]] is a test person.'
>>> _privatize_line("When we talk about [[MsAlice]] in the middle of a " \
"sentence, that's fine too.", {'MsAlice': 'A.'})
"When we talk about [[A.|PrivatePerson]] in the middle of a sentence, that's fine too."
Content inside a macro:
>>> _privatize_line('''Text with a footnote.''' \
'''<<fnote "Here's my footnote about MsAlice.">>''', \
{'MsAlice': 'A.'})
'Text with a footnote.<<fnote "Here\\'s my footnote about [[A.|PrivatePerson]].">>'
Links with different text and target:
>>> _privatize_line("We can talk about [[Alice|MsAlice]] " \
"with different text.", {'MsAlice': 'A.'})
'We can talk about [[Alice|PrivatePerson]] with different text.'
Multiple replacements with different people:
>>> _privatize_line("We can have [[MsAlice]] and MrBob talk to each other " \
"in the same line.", {'MsAlice': 'A.', 'MrBob': 'B.'})
'We can have [[A.|PrivatePerson]] and [[B.|PrivatePerson]] talk to each other in the same line.'
Multiple replacements with the same person:
>>> _privatize_line("We can have MsAlice talk to herself (MsAlice) " \
"in the same line.", {'MsAlice': 'A.'})
'We can have [[A.|PrivatePerson]] talk to herself ([[A.|PrivatePerson]]) in the same line.'
>>> _privatize_line("Likewise [[MsAlice]] can do it with brackets " \
"([[MsAlice]]).", {'MsAlice': 'A.'})
'Likewise [[A.|PrivatePerson]] can do it with brackets ([[A.|PrivatePerson]]).'
>>> _privatize_line('We can talk about [[Alice|MsAlice]] lots of ways, ' \
'like MsAlice and [[MsAlice]].', {'MsAlice': 'A.'})
'We can talk about [[Alice|PrivatePerson]] lots of ways, like [[A.|PrivatePerson]] and [[A.|PrivatePerson]].'
Replacements with alternate link text:
>>> _privatize_line('We can talk about [[Alice|MsAlice]] and [[Bob|MrBob]] as well', \
{'MsAlice': 'A.', 'MrBob': 'B.'}, replace_link_text=True)
'We can talk about [[A.|PrivatePerson]] and [[B.|PrivatePerson]] as well'
We don't want to replace places where a CamelCase match is a substring of another
word. This is expected to yield no output because there's nothing to replace:
>>> _privatize_line("But an EmbeddedCamelWithMsAliceInIt isn't her.", \
{'MsAlice': 'A.'})
"""
def iteroccurrences(needle: str) -> Generator[int, int, None]:
"""
Iterate over the start indices of occurrences of substring
``needle`` in the line /line/ in outer scope.
(We have to use outer scope because it can be changed while we're iterating
and the generator is only bound to arguments once.)
"""
idx = -1
while True:
idx = line.find(needle, idx + 1)
if idx == -1:
return
else:
additional_increments = yield idx
if additional_increments is not None:
idx += additional_increments
def anchored_at_one_end(start_index: int, end_index: int) -> bool:
return start_index == 0 or end_index == len(line)
def is_camelcase_link(start_index: int, end_index: int) -> bool:
return (anchored_at_one_end(start_index, end_index)
or (line[start_index-1] != '[' and line[end_index] != ']'))
def is_bare_bracketed_link(start_index: int, end_index: int) -> bool:
return (not anchored_at_one_end(start_index, end_index)
and line[start_index-2:start_index] == '[['
and line[end_index:end_index+2] == ']]')
def is_textual_bracketed_link(start_index: int, end_index: int) -> bool:
return (not anchored_at_one_end(start_index, end_index)
and line[start_index-1] == '|'
and line[end_index:end_index+2] == ']]')
dirty = False
increment_iterator_by = 0
for replace_person, replace_initials in replacement_table.items():
iterator = iteroccurrences(replace_person)
try:
while True:
# NOTE: the "end" index is one after the last index in the string,
# as is needed for slice notation.
if increment_iterator_by:
start_idx = iterator.send(increment_iterator_by)
increment_iterator_by = 0
else:
start_idx = next(iterator)
end_idx = start_idx + len(replace_person)
new_line = None
if is_camelcase_link(start_idx, end_idx):
# camel-case link or unlinked reference in text
def is_spurious_substring():
# If there's not a non-alphanumeric character on both sides of
# the "link", we may be making a clbuttic replacement.
# <https://en.wikipedia.org/wiki/Scunthorpe_problem>
start_ok = start_idx == 0 or not line[start_idx-1].isalnum()
end_ok = end_idx == len(line) or not line[end_idx].isalnum()
return not (start_ok and end_ok)
if not is_spurious_substring():
new_line = (line[0:start_idx]
+ f'[[{replace_initials}|PrivatePerson]]'
+ line[end_idx:])
elif is_bare_bracketed_link(start_idx, end_idx):
# link with the person as the target and text
replacement = replace_initials + '|PrivatePerson'
new_line = line[0:start_idx] + replacement + line[end_idx:]
elif is_textual_bracketed_link(start_idx, end_idx):
# link with the person as the target only;
# beware that you might have put something private in the text
if replace_link_text:
start_of_link = line[0:start_idx].rfind('[[', 0, start_idx) + 2
new_line = line[0:start_of_link] + f"{replace_initials}|PrivatePerson" + line[end_idx:]
else:
new_line = line[0:start_idx] + 'PrivatePerson' + line[end_idx:]
else:
link = line[start_idx:end_idx]
raise ValueError("Unknown type of link '{link}'.")
if new_line:
line = new_line
dirty = True
# If we changed the length of the string by modifying it,
# we need to update our stored position within the string.
increment_iterator_by = len(new_line) - len(line)
except StopIteration:
pass
if dirty:
return line
else:
return None
@tzk_builder
def replace_private_people(initialer: Callable[[str], str] = None) -> None:
def replace_private_people(initialer: Callable[[str], str] = None, replace_link_text: bool = False) -> None:
"""
Replace the names of people who are not marked Public with their initials.
@ -386,37 +542,36 @@ def replace_private_people(initialer: Callable[[str], str] = None) -> None:
that takes one string argument
(a tiddler filename without the full path, e.g., ``MsJaneDoe.tid``)
and returns a string to be considered the "initials" of that person.
:param replace_link_text: If you have links in the form
``So then [[John said|MrJohnDoe]] something about this``,
then enabling this option ensures that the link is fully
replaced with
``So then [[J.D.|PrivatePerson]] something about this``.
This means that when using this feature, having the
link text also be meaningful after redaction is important.
.. warning ::
Using this link replacement feature does not redact everything, just the link
(and the link text with `replace_link_text` enabled). So *do not* rely on it
for redacting everything. Making a tiddler public still needs consideration and
tooling is there to help, not to replace your own judgment.
"""
assert 'public_wiki_folder' in build_state
replacement_table = _private_people_replacement_table(initialer)
tid_files = (Path(build_state['public_wiki_folder']) / "tiddlers").glob("**/*.tid")
root = (Path(build_state['public_wiki_folder']) / "tiddlers")
tid_files = itertools.chain(root.glob("**/*.tid"), root.glob("**/*.json"))
for tiddler in tid_files:
dirty = False
with tiddler.open() as f:
lines = f.readlines()
for idx, line in enumerate(lines):
for replace_person, replace_initials in replacement_table.items():
if replace_person in line:
if '|' + replace_person + ']]' in line:
# link with the person as the target only;
# beware that you might have put something private in the text
lines[idx] = line.replace(replace_person,
'PrivatePerson')
elif '[[' + replace_person + ']]' in line:
# link with the person as the target and text
lines[idx] = line.replace(replace_person,
replace_initials + '|PrivatePerson')
else:
# camel-case link or unlinked reference in text;
# or spurious substring, so rule that out with the '\b' search
lines[idx] = re.sub(
r"\b" + re.escape(replace_person) + r"\b",
f'<<privateperson "{replace_initials}">>',
line
)
dirty = True
for i in range(len(lines)):
private_line = _privatize_line(lines[i], replacement_table, replace_link_text)
if private_line is not None:
lines[i] = private_line
dirty = True
if dirty:
with tiddler.open("w") as f:
f.writelines(lines)

View File

@ -35,6 +35,10 @@ commit_remote = ""
# http://localhost:8080 in your browser.
listen_port = 8080
# Host to listen on. If you specify "0.0.0.0" it will listen to all network interfaces.
# This is useful for allowing the wiki to be exposed to the network through a container.
listen_host = "127.0.0.1"
# Uncomment if you want to require HTTP basic authentication when serving your wiki.
# **WARNING**: this is NOT secure for use over the open Internet or all but the
# simplest local networks, as the password is sent in the clear. For good

View File

@ -0,0 +1,31 @@
{
"journals":{
"lastDayOfWeek":"0",
"formatter":"$:/macros/bj/Calendar/journalfmt.js",
"titlebold":"no",
"highlightThisDay":"no",
"highlightThisDate":"yes"
},
"default":{
"lastDayOfWeek":"0",
"formatter":"$:/macros/bj/Calendar/journalslinked.js",
"titlebold":"yes",
"highlightThisDay":"no",
"highlightLinks":"yes"
},
"diary":{
"lastDayOfWeek":"0",
"formatter":"$:/macros/bj/Calendar/diary.js",
"titlebold":"yes",
"highlightThisDay":"no",
"highlightLinks":"yes"
},
"plain":{
"lastDayOfWeek":"0",
"formatter":"",
"titlebold":"yes",
"highlightThisDay":"yes",
"highlightThisDate":"no"
}
}

View File

@ -0,0 +1,3 @@
module-type: library
title: $:/config/bj/Calendar.json
type: application/json

View File

@ -0,0 +1,96 @@
/*\
title: $:/core/modules/widgets/macrorefresh.js
type: application/javascript
module-type: widget
Macrocall widget
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var MacroCallWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
};
/*
Inherit from the base widget class
*/
MacroCallWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
MacroCallWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
MacroCallWidget.prototype.execute = function() {
var self = this;
// Get the parse type if specified
this.parseType = this.getAttribute("$type","text/vnd.tiddlywiki");
this.renderOutput = this.getAttribute("$output","text/html");
// Merge together the parameters specified in the parse tree with the specified attributes
this.params = this.parseTreeNode.params ? this.parseTreeNode.params.slice(0) : [];
$tw.utils.each(this.attributes,function(attribute,name) {
if(name.charAt(0) !== "$") {
self.params.push({name: name, value: attribute});
}
});
// Get the macro value
var text = this.getVariable(this.parseTreeNode.name || this.getAttribute("$name"),{params: this.params}),
parseTreeNodes;
// Are we rendering to HTML?
if(this.renderOutput === "text/html") {
// If so we'll return the parsed macro
var parser = this.wiki.parseText(this.parseType,text,
{parseAsInline: !this.parseTreeNode.isBlock});
parseTreeNodes = parser ? parser.tree : [];
} else {
// Otherwise, we'll render the text
var plainText = this.wiki.renderText("text/plain",this.parseType,text,{parentWidget: this});
parseTreeNodes = [{type: "text", text: plainText}];
}
// Construct the child widgets
this.makeChildWidgets(parseTreeNodes);
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
MacroCallWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if($tw.utils.count(changedAttributes) > 0) {
// Rerender ourselves
this.refreshSelf();
return true;
}
//else
if (this.getAttribute("$refresh")) {
var params = this.params.slice(0);
params.push({name: "changedTiddlers", value: changedTiddlers});
if (this.getVariable(this.getAttribute("$refresh"),{params: params})) {
// Rerender ourselves
this.refreshSelf();
return true;
}
//else
}
return this.refreshChildren(changedTiddlers);
};
exports.macrorefresh = MacroCallWidget;
})();

View File

@ -0,0 +1,3 @@
module-type: widget
title: $:/core/modules/widgets/macrorefresh.js
type: application/javascript

View File

@ -0,0 +1,37 @@
/*\
title: $:/macros/bj/Calendar/diary.js
type: application/javascript
module-type: global
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
calendar demo
*/
var Calendar=new Date();
var createMonth= function(mnth,year,options){
var month=[];
for (var i=1;i < 1+daysInMonth(mnth,year);i++) month[i] = createDate(i,mnth,year,options);
return month;
}
function createDate(i,mnth,year,options){
var strong='',tiddlerDate,format = $tw.wiki.getTiddlerText("$:/config/NewJournal/Title") || "YYYY MM DD";
var date=(new Date(year, mnth-1, i));
if (options.highlightLinks=="yes") strong ='!';
tiddlerDate = $tw.utils.formatDateString(date,format);
if ($tw.wiki.getTiddler(tiddlerDate))return centre(strong+'[['+i+'|'+tiddlerDate+']]');
return centre('[['+i+'|'+tiddlerDate+']]');
}
function daysInMonth(iMonth, iYear){
return 32 - new Date(iYear, iMonth-1, 32).getDate();
}
function centre (x){ return ' '+x+' ';}
exports.createMonth = createMonth;
})();

View File

@ -0,0 +1,3 @@
module-type: global
title: $:/macros/bj/Calendar/diary.js
type: application/javascript

View File

@ -0,0 +1,37 @@
/*\
title: $:/macros/bj/Calendar/journalfmt.js
type: application/javascript
module-type: global
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
calendar demo
*/
var Calendar=new Date();
var createMonth= function(mnth,year,options){
var month=[];
for (var i=1;i < 1+daysInMonth(mnth,year);i++) month[i] = createDate(i,mnth,year,options);
return month;
}
function createDate(i,mnth,year,options){
var strong='',tiddlerDate;
var date=(new Date(year, mnth, i));
if (date.toDateString()===Calendar.toDateString()&&options.highlightThisDate=="yes") strong ='!';
tiddlerDate=date.getDate()+
' '+$tw.language.getString("Date/Long/Month/" + (date.getMonth()))+' '+date.getFullYear();
if ($tw.wiki.getTiddler(tiddlerDate)!==undefined) strong ='!';
return centre(strong+'[['+i+'|'+tiddlerDate+']]');
}
function daysInMonth(iMonth, iYear){
return 32 - new Date(iYear, iMonth, 32).getDate();
}
function centre (x){ return ' '+x+' ';}
exports.createMonth = createMonth;
})();

View File

@ -0,0 +1,3 @@
module-type: global
title: $:/macros/bj/Calendar/journalfmt.js
type: application/javascript

View File

@ -0,0 +1,37 @@
/*\
title: $:/macros/bj/Calendar/journalslinked.js
type: application/javascript
module-type: global
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
calendar demo
*/
var Calendar=new Date();
var createMonth= function(mnth,year,options){
var month=[];
for (var i=1;i < 1+daysInMonth(mnth,year);i++) month[i] = createDate(i,mnth,year,options);
return month;
}
function createDate(i,mnth,year,options){
var strong='',tiddlerDate,format = $tw.wiki.getTiddlerText("$:/config/NewJournal/Title") || "YYYY MM DD";
var date=(new Date(year, mnth-1, i));
if (options.highlightLinks=="yes") strong ='!';
tiddlerDate = $tw.utils.formatDateString(date,format);
if ($tw.wiki.getTiddler(tiddlerDate))return centre(strong+'[['+i+'|'+tiddlerDate+']]');
return i;
}
function daysInMonth(iMonth, iYear){
return 32 - new Date(iYear, iMonth-1, 32).getDate();
}
function centre (x){ return ' '+x+' ';}
exports.createMonth = createMonth;
})();

View File

@ -0,0 +1,3 @@
module-type: global
title: $:/macros/bj/Calendar/journalslinked.js
type: application/javascript

View File

@ -0,0 +1,140 @@
/*\
title: $:/macros/buggyj/Calendar/base.js
type: application/javascript
module-type: macro
<<diary year month>>
<<diary year>> - year calendar
<<diary>> - this month
Options:$:/macros/diary/options.json
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
CAL demo
*/
exports.name = "calendarbase";
exports.params = [
{ name: "year" },{ name: "month" },{ name: "opts" }
];
/*
Run the macro
*/
exports.run = function(year, month,opts) {
if (!opts) opts="default";
var options = $tw.wiki.getTiddlerData("$:/config/bj/Calendar.json")[opts]||
{lastDayOfWeek:"6",formatter:"",titlebold:"",highlightThisDay:"",highlightThisDate:""};
var createMonth;
try {
createMonth = require(options.formatter).createMonth;
} catch (e) {
createMonth= function(mnth,year){
var month=[];
for (var i=1;i < 1+daysInMonth(mnth,year);i++) month[i] = i;
return month;
}
}
var boldtitle=(options.titlebold=='yes')?'!':'';
var day_of_week = (function () {
var days = [];
for (var i = 0; i < 7; i++) {days[i] = $tw.language.getString("Date/Short/Day/" + i); }
return days;
})();
var month_of_year = (function () {
var months = [];
for (var i = 1; i < 13; i++) {months[i] = $tw.language.getString("Date/Long/Month/" + i); }
return months;
})();
var Calendar = new Date();
var thisyear = Calendar.getFullYear(); // year (xxxx)
var thismonth = Calendar.getMonth()+1; // month (0-11)
var thisday = Calendar.getDay(); // day (0-6)
var WEEKFIN = parseInt(options.lastDayOfWeek);
var MONTHS_IN_YEAR=12;
var lf ='\n';
var cal='<div>'+lf+lf;
var ayear=thisyear;
if (!!month) {
if (!!year) {
cal+=calendar (month,year,options);
} else {
cal+=calendar (month,thisyear,options);
}
} else {
if (!!year) {
cal+=titleOfYear(year);
options.seperateYearHeading = 'yes';
ayear=year;
for(var i=0; i<MONTHS_IN_YEAR; i+=2)
cal+=splicetable(calendar (i+1,ayear,options),calendar (i+2,ayear,options));
}
else {
cal+=calendar (thismonth,thisyear,options);
}
}
return cal+lf+lf+'</div>';
function calendar (mnth,year,options){
var month = createMonth(mnth,year,options);
var blankdays = (firstDayInMonth(mnth,year)+13-WEEKFIN)%7;
return titleOfMonth(mnth,year)+createWeekHeading()+
formatAsMonth(month,blankdays);
}
function titleOfMonth(mth,year) {
if (!!options.seperateYearHeading ) {
return '|>|>|>|'+ centre(boldtitle+ month_of_year[mth]) +'|<|<|<|'+lf;
} else {
return '|>|>|>|'+ centre(boldtitle+ month_of_year[mth] + ' ' + year) +'|<|<|<|'+lf;
}
}
function titleOfYear(year) {
return '|>|>|>|>|>|>|>|'+ centre('!'+year) +'|<|<|<|<|<|<|<|'+lf;
}
function centre (x){ return ' '+x+' ';}
function formatAsMonth(month,blankdays){
var theday,blank=['','|','||','|||','||||','|||||','||||||','|||||||'];
var cal=blank[blankdays];//pad out before first day of month
for(var i=1; i < month.length;i++){//first '0' month element is not used
cal+='|'+month[i];
theday=(i+blankdays-1)%7;
if (theday == 6) cal += '|' + lf;
}
if (theday !== 6) cal += blank[7 - theday] + lf;//pad out rest of week, if needed
return cal ;
}
function createWeekHeading(){
var daystitle=[],weekdays= day_of_week.slice(0);
// highlight today's day of week
if (options.highlightThisDay=='yes')weekdays[thisday] ='!'+weekdays[thisday];
for (var i=0;i < weekdays.length; i++) daystitle[i] =centre(weekdays[(i+1+WEEKFIN)%7]);
return '|'+daystitle.join('|')+'|'+lf;
}
function daysInMonth(iMonth, iYear){
return 32 - new Date(iYear, iMonth-1, 32).getDate();
}
function firstDayInMonth(iMonth, iYear){
return new Date(iYear, iMonth-1, 1).getDay();
}
function splicetable (a,b) {
var i,cal='',taba =a.split('\n'),tabb=b.split('|\n');
var limit=(taba.length<tabb.length)?taba.length:tabb.length;//shortest
for (i=0;i<limit-1;i++) cal+=taba[i]+tabb[i]+'|'+lf;
for (;i < taba.length-1;i++) cal+=taba[i]+"||||||||"+lf;
for (;i < tabb.length-1;i++) cal+="||||||||"+tabb[i]+lf;
return cal;
}
}
})();

View File

@ -0,0 +1,3 @@
module-type: macro
title: $:/macros/buggyj/Calendar/base.js
type: application/javascript

View File

@ -0,0 +1,36 @@
/*\
title: $:/macros/buggyj/Calendar/entry.js
type: application/javascript
module-type: macro
<<calendar year month>>
<<calendar year>> - year calendar
<<calendar>> - this month
Options:$:/macros/diary/options.json
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
CAL demo
*/
exports.name = "calendar";
exports.params = [
{ name: "year" },{ name: "month" },{ name: "opts" }
];
/*
Run the macro
*/
exports.run = function(year, month,opts) {
return '<$macrorefresh $name="calendarbase" year="'+year+'" month="'+month+'" opts="'+opts+'" $refresh="calendarrefresh"/>';
}
})();

View File

@ -0,0 +1,3 @@
module-type: macro
title: $:/macros/buggyj/Calendar/entry.js
type: application/javascript

View File

@ -0,0 +1,39 @@
/*\
title: $:/macros/buggyj/Calendar/entry2.js
type: application/javascript
module-type: macro
<<diary year month>>
<<diary year>> - year calendar
<<diary>> - this month
Options:$:/macros/diary/options.json
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
CAL demo
*/
exports.name = "diary";
exports.params = [
{ name: "year" },{ name: "month" },{ name: "opts" }
];
/*
Run the macro
*/
exports.run = function(year, month,opts) {
var tags = $tw.wiki.getTiddlerText("$:/config/NewJournal/Tags");
if (!opts) opts = "diary";
return '<$ifnew fields="""{"tags":"'+tags+'"}""">' +
'<$macrorefresh $name="calendarbase" year="'+year+'" month="'+month+'" opts="'+opts+'" $refresh="calendarrefresh"/>'+ '</$ifnew>';
}
})();

View File

@ -0,0 +1,3 @@
module-type: macro
title: $:/macros/buggyj/Calendar/entry2.js
type: application/javascript

View File

@ -0,0 +1,87 @@
/*\
title: $:/macros/buggyj/Calendar/ifnew.js
type: application/javascript
module-type: widget
Linkcatcher widget
ToDo - add message param to listen for other mssg and action to set other actions (link just create)
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var Widget = require("$:/core/modules/widgets/widget.js").widget;
var IfNewWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
this.addEventListeners([
{type: "tm-navigate", handler: "handleNavigateEvent"}
]);
};
/*
Inherit from the base widget class
*/
IfNewWidget.prototype = new Widget();
/*
Render this widget into the DOM
*/
IfNewWidget.prototype.render = function(parent,nextSibling) {
this.parentDomNode = parent;
this.computeAttributes();
this.execute();
this.renderChildren(parent,nextSibling);
};
/*
Compute the internal state of the widget
*/
IfNewWidget.prototype.execute = function() {
// Get our parameters
this.fields = this.getAttribute("fields");
this.catchMessage = this.getAttribute("message");
// Construct the child widgets
this.makeChildWidgets();
};
/*
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
IfNewWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
if(changedAttributes.fields || changedAttributes.message ) {
this.refreshSelf();
return true;
} else {
return this.refreshChildren(changedTiddlers);
}
};
/*
Handle a tm-navigate event
*/
IfNewWidget.prototype.handleNavigateEvent = function(event) {
var tiddler = this.wiki.getTiddler(event.navigateTo),fds;
if(tiddler) return true;
try {
fds=JSON.parse(this.fields);
} catch(e) {
fds={};
}
fds.title = event.navigateTo;
if(this.parentWidget) {
this.parentWidget.dispatchEvent({
type: "tm-new-tiddler",
param: fds
});
}
return false;
};
exports.ifnew = IfNewWidget;
})();

View File

@ -0,0 +1,3 @@
module-type: widget
title: $:/macros/buggyj/Calendar/ifnew.js
type: application/javascript

View File

@ -0,0 +1,105 @@
/*\
title: $:/macros/buggyj/Calendar/refresh.js
type: application/javascript
module-type: macro
<<diary year month>>
<<diary year>> - year calendar
<<diary>> - this month
Options:$:/macros/diary/options.json
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
CAL demo
*/
exports.name = "calendarrefresh";
exports.params = [
{ name: "year" },{ name: "month" },{ name: "opts" },{name:"changedTiddlers"}
];
// Source: http://stackoverflow.com/questions/497790
var dates = {
convert:function(d) {
// Converts the date in d to a date-object. The input can be:
// a date object: returned without modification
// an array : Interpreted as [year,month,day]. NOTE: month is 0-11.
// a number : Interpreted as number of milliseconds
// since 1 Jan 1970 (a timestamp)
// a string : Any format supported by the javascript engine, like
// "YYYY/MM/DD", "MM/DD/YYYY", "Jan 31 2009" etc.
// an object : Interpreted as an object with year, month and date
// attributes. **NOTE** month is 0-11.
return (
d.constructor === Date ? d :
d.constructor === Array ? new Date(d[0],d[1],d[2]) :
d.constructor === Number ? new Date(d) :
d.constructor === String ? new Date(d) :
typeof d === "object" ? new Date(d.year,d.month,d.date) :
NaN
);
},
compare:function(a,b) {
// Compare two dates (could be of any type supported by the convert
// function above) and returns:
// -1 : if a < b
// 0 : if a = b
// 1 : if a > b
// NaN : if a or b is an illegal date
// NOTE: The code inside isFinite does an assignment (=).
return (
isFinite(a=this.convert(a).valueOf()) &&
isFinite(b=this.convert(b).valueOf()) ?
(a>b)-(a<b) :
NaN
);
},
inRange:function(d,start,end) {
// Checks if date in d is between dates in start and end.
// Returns a boolean or NaN:
// true : if d is between start and end (inclusive)
// false : if d is before start or after end
// NaN : if one or more of the dates is illegal.
// NOTE: The code inside isFinite does an assignment (=).
return (
isFinite(d=this.convert(d).valueOf()) &&
isFinite(start=this.convert(start).valueOf()) &&
isFinite(end=this.convert(end).valueOf()) ?
start <= d && d <= end :
NaN
);
}
}
/*
Run the macro
*/
exports.run = function(year, month,opts,changedTiddlers) {
var found = false;
var journaltag = "Journal";
$tw.utils.each(changedTiddlers,function(attribute,name) {
if (attribute.deleted) {
return;
}
var tiddler = $tw.wiki.getTiddler(name);
var tags = (tiddler.fields.tags || []).slice(0);
if(tags.indexOf(journaltag) != -1) {
found = true;
}
});
if (found) return "found";
return "";
}
})();

View File

@ -0,0 +1,3 @@
module-type: macro
title: $:/macros/buggyj/Calendar/refresh.js
type: application/javascript

View File

@ -0,0 +1,22 @@
title: $:/plugins/bj/Calendar/license
The MIT License (MIT)
Copyright (c) 2014 Jeffrey Wikinson aka buggyj
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,4 @@
title: $:/plugins/bj/Calendar/readme
For documentation see the project home at
http://bjtools.tiddlyspot.com#ExtendableCalendar

View File

@ -0,0 +1,13 @@
caption: Calendar
tags: $:/tags/SideBar
title: $:/plugins/bj/calendar/sidbar
type: text/vnd.tiddlywiki
<$reveal state="$:/temp/plugins/bj/Calendar" text="Year" type="nomatch">
<$button set="$:/temp/plugins/bj/Calendar" setTo="Year">Year</$button>
<<calendar "" "">>
</$reveal>
<$reveal state="$:/temp/plugins/bj/Calendar" text="Year" type="match">
<$button set="$:/temp/plugins/bj/Calendar" setTo="Month">Month</$button>
<<calendar 2014 "">>
</$reveal>

View File

@ -0,0 +1,11 @@
{
"author": "JeffreyWilkinson",
"core-version": ">=5.1.11",
"dependents": "",
"description": "calendar with date formatter addons",
"list": "readme license",
"plugin-type": "plugin",
"source": "https://github.com/buggyj/TW5-tools",
"title": "$:/plugins/bj/Calendar",
"version": "1.11.0"
}

View File

@ -9,5 +9,6 @@
"plugin-type": "plugin",
"source": "https://github.com/TheDiveO/TW5FontAwesome",
"title": "$:/plugins/TheDiveO/FontAwesome",
"version": "1.2.18"
"version": "1.2.18",
"dependents": ""
}

View File

@ -1,93 +0,0 @@
created: 20200510004110231
modified: 20200730205800000
tags: $:/tags/Macro
title: $:/plugins/sobjornstad/TiddlyRemember/macros/remember
type: text/vnd.tiddlywiki
\define remembertwo(id, reference)
<div class="tr-selfidentification">
<$set name="selfid" filter="""[enlist[$reference$]]""" value="""[<$link to="$reference$">$reference$</$link>: $id$]""" emptyValue="[$id$]">
<<selfid>>
</$set>
</div>
<div class="rid">
[$id$]
</div>
<div class="tr-reference">
<$text text=<<__reference__>>/>
</div>
\end
\define rememberq(id, question, answer, reference: "")
<div class="rememberq remembertwo">
<div class="rquestion tr-ritem">
<div>Q:</div>
<p>$question$</p>
</div>
<div class="ranswer tr-ritem">
<div>A:</div>
<p>$answer$</p>
</div>
<$macrocall $name=remembertwo id=<<__id__>> reference=<<__reference__>>/>
</div>
\end
\define rememberp(id, first, second, reference: "")
<div class="rememberp remembertwo">
<div class="rfirst tr-ritem">
<div>1:</div>
<p>$first$</p>
</div>
<div class="rsecond tr-ritem">
<div>2:</div>
<p>$second$</p>
</div>
<$macrocall $name=remembertwo id=<<__id__>> reference=<<__reference__>>/>
</div>
\end
\define remembercz(id, text, mode: "block", reference: "")
<$list filter="[[$mode$]match[inline]]">
<$macrocall $name=twRememberClozeInline id=<<__id__>> text=<<__text__>> reference=<<__reference__>>/>
</$list>
<$list filter="[[$mode$]!match[inline]]">
<$macrocall $name=twRememberClozeBlock id=<<__id__>> text=<<__text__>> reference=<<__reference__>>/>
</$list>
\end
\define twRememberClozeBlock(id, text, reference)
<div class="remembercz">
<span class="cloze-identifier">cloze: </span>
<span class="cloze-text">$text$</span>
<div class="tr-selfidentification">
<$set name="selfid" filter="""[enlist[$reference$]]""" value="""[<$link to="$reference$">$reference$</$link>: $id$]""" emptyValue="[$id$]">
<<selfid>>
</$set>
</div>
<div class="rid">
[$id$]
</div>
<div class="tr-reference">
<$text text=<<__reference__>>/>
</div>
</div>
\end
\define twRememberClozeInline(id, text, reference)
<span class="remembercz">
<span class="cloze-identifier">{cloze: </span>
<span class="cloze-text">$text$</span>
<span class="cloze-identifier">}</span>
<div class="tr-selfidentification">
<$set name="selfid" filter="""[enlist[$reference$]]""" value="""[<$link to="$reference$">$reference$</$link>: $id$]""" emptyValue="[$id$]">
<<selfid>>
</$set>
</div>
<div class="rid">
[$id$]
</div>
<div class="tr-reference">
<$text text=<<__reference__>>/>
</div>
</span>
\end

View File

@ -1,30 +0,0 @@
caption: RememberQ
condition: [<targetTiddler>!has[type]] [<targetTiddler>type[text/vnd.tiddlywiki]]
created: 20161118171456554
description: Remember Question/Answer
icon: $:/plugins/sobjornstad/TiddlyRemember/icons/lightbulb-white.svg
modified: 20200517161709608
shortcuts: ((rememberq))
tags: $:/tags/EditorToolbar
title: $:/plugins/sobjornstad/TiddlyRemember/toolbar/rememberq
type: text/vnd.tiddlywiki
\define getMacro()
<<rememberq "$(questionid)$"
"Q"
"A">>
\end
\define prefixId() $(theprefix)$$(thetime)$
<$set name=thetime value=<<now "[UTC]YYYY0MM0DD0hh0mm0ss0XXX">> >
<$set name=theprefix value={{$:/config/TiddlyRemember/IdPrefix}}>
<$set name=questionid value=<<prefixId>> >
<$action-sendmessage
$message="tm-edit-text-operation"
$param="replace-selection"
text=<<getMacro>>
/>
</$set>
</$set>
</$set>

View File

@ -0,0 +1,5 @@
created: 20210925145600000
modified: 20210925145600000
tags:
title: $:/config/TiddlyRemember/DefaultClasses
type: text/vnd.tiddlywiki

View File

@ -0,0 +1,7 @@
created: 20210929144900000
modified: 20210929144900000
tags:
title: $:/config/TiddlyRemember/snippets/remembercz
<<remembercz "%NOTE_ID%"
"Cloze">>

View File

@ -0,0 +1,8 @@
created: 20210929144900000
modified: 20210929144900000
tags:
title: $:/config/TiddlyRemember/snippets/rememberq
<<rememberq "%NOTE_ID%"
"Q"
"A">>

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2020 Soren Bjornstad.
Copyright (c) 2020-2021 Soren Bjornstad and the TiddlyRemember community.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -0,0 +1,20 @@
created: 20210929144300000
modified: 20210929144300000
tags: $:/tags/Macro
title: $:/plugins/sobjornstad/TiddlyRemember/macros/insert-snippet
type: text/vnd.tiddlywiki
\define tr-insert-note(snippet)
<$vars
thetime=<<now "[UTC]YYYY0MM0DD0hh0mm0ss0XXX">>
theprefix={{$:/config/TiddlyRemember/IdPrefix}}>
<$vars
noteid={{{ [<theprefix>addsuffix<thetime>] }}}
editingtid={{{ [<storyTiddler>get[draft.of]] }}}>
<$action-sendmessage
$message="tm-edit-text-operation"
$param="replace-selection"
text={{{ [<__snippet__>search-replace[%NOTE_ID%],<noteid>search-replace[%EDITING_TIDDLER%],<editingtid>] }}}/>
</$vars>
</$vars>
\end

View File

@ -0,0 +1,95 @@
created: 20200510004110231
modified: 20200925141800000
tags: $:/tags/Macro
title: $:/plugins/sobjornstad/TiddlyRemember/macros/remember
type: text/vnd.tiddlywiki
\define remembertwo(id, reference, sched)
<div class="tr-selfidentification">
<$set name="selfid" filter="""[enlist[$reference$]]""" value="""[<$link to="$reference$">$reference$</$link>: $id$]""" emptyValue="[$id$]">
<<selfid>>
</$set>
</div>
<div class="rid">
[$id$]
</div>
<div class="tr-reference">
<$text text=<<__reference__>>/>
</div>
<div class="tr-sched">
<$text text=<<__sched__>>/>
</div>
\end
\define rememberq(id, question, answer, reference: "", sched: "", class:"")
<div class={{{ [[rememberq remembertwo ]addsuffix<__class__>addsuffix[ ]addsuffix{$:/config/TiddlyRemember/DefaultClasses}] }}}>
<div class="rquestion tr-ritem">
<div>Q:</div>
<p>$question$</p>
</div>
<div class="ranswer tr-ritem">
<div>A:</div>
<p>$answer$</p>
</div>
<$macrocall $name=remembertwo id=<<__id__>> reference=<<__reference__>> sched=<<__sched__>>/>
</div>
\end
\define rememberp(id, first, second, reference: "", sched: "", class: "")
<div class={{{ [[rememberp remembertwo ]addsuffix<__class__>addsuffix[ ]addsuffix{$:/config/TiddlyRemember/DefaultClasses}] }}}>
<div class="rfirst tr-ritem">
<div>1:</div>
<p>$first$</p>
</div>
<div class="rsecond tr-ritem">
<div>2:</div>
<p>$second$</p>
</div>
<$macrocall $name=remembertwo id=<<__id__>> reference=<<__reference__>> sched=<<__sched__>>/>
</div>
\end
\define remembercz(id, text, mode: "block", reference: "", sched: "", class: "")
<$list filter="[[$mode$]match[inline]]" variable=_>
<$macrocall $name=twRememberClozeInline id=<<__id__>> text=<<__text__>> reference=<<__reference__>> sched=<<__sched__>> class=<<__class__>>/>
</$list>
<$list filter="[[$mode$]!match[inline]]" variable=_>
<$macrocall $name=twRememberClozeBlock id=<<__id__>> text=<<__text__>> reference=<<__reference__>> sched=<<__sched__>> class=<<__class__>>/>
</$list>
\end
\define twRememberMetadata(id, reference, sched)
<div class="tr-selfidentification">
<$set name="selfid" filter="""[enlist[$reference$]]""" value="""[<$link to="$reference$">$reference$</$link>: $id$]""" emptyValue="[$id$]">
<<selfid>>
</$set>
</div>
<div class="rid">
[$id$]
</div>
<div class="tr-reference">
<$text text=<<__reference__>>/>
</div>
<div class="tr-sched">
<$text text=<<__sched__>>/>
</div>
\end
\define twRememberClozeBlock(id, text, reference, sched, class)
<div class={{{ [[remembercz ]addsuffix<__class__>addsuffix[ ]addsuffix{$:/config/TiddlyRemember/DefaultClasses}] }}}>
<span class="cloze-identifier"><span class="tr-name-cloze">cloze: </span></span>
<span class="cloze-display"><$set name="unescape" value={{{ [<__text__>search-replace:g[\{],[{]search-replace:g[\}],[}]] }}}><<unescape>></$set></span>
<span class="cloze-text">$text$</span>
<$macrocall $name="twRememberMetadata" id=<<__id__>> reference=<<__reference__>> sched=<<__sched__>>/>
</div>
\end
\define twRememberClozeInline(id, text, reference, sched, class)
<span class={{{ [[remembercz ]addsuffix<__class__>addsuffix[ ]addsuffix{$:/config/TiddlyRemember/DefaultClasses}] }}}>
<span class="cloze-identifier">{<span class="tr-name-cloze">cloze: </span></span>
<span class="cloze-display"><$set name="unescape" value={{{ [<__text__>search-replace:g[\{],[{]search-replace:g[\}],[}]] }}}><<unescape>></$set></span>
<span class="cloze-identifier">}</span>
<span class="cloze-text">$text$</span>
<$macrocall $name="twRememberMetadata" id=<<__id__>> reference=<<__reference__>> sched=<<__sched__>>/>
</span>
\end

View File

@ -43,11 +43,37 @@ div.remembercz {
color: <<colour muted-foreground>>;
}
/* the separate reference and ID fields are only for TR's benefit */
/* these separate fields are only read by the TR parser */
div.remembertwo div.tr-reference, .remembercz div.tr-reference {
display: none;
}
div.remembertwo div.rid, .remembercz div.rid {
display: none;
}
div.remembertwo div.tr-sched, .remembercz div.tr-sched {
display: none;
}
.remembercz > span.cloze-text {
display: none;
}
/***** Built-in classes for common format customizations; apply to the class: parameter to macros *****/
/* Hide the ID field */
.tr-hide-id div.tr-selfidentification {
display: none;
}
/* Hide the ID only if it's an inline cloze */
span.remembercz.tr-hide-inline-cloze-id div.tr-selfidentification {
display: none;
}
/* Hide the word "cloze" on inline clozes */
span.remembercz.tr-hide-inline-cloze-name span.cloze-identifier span.tr-name-cloze {
display: none;
}

View File

@ -6,9 +6,13 @@ type: text/vnd.tiddlywiki
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
<span id="tr-version">1.2.2</span>
<$set name="tr-rendering" value="yes">
<span id="tr-version">1.3.4</span>
{{||$:/plugins/sobjornstad/TiddlyRemember/templates/AnkiDecks}}
{{||$:/plugins/sobjornstad/TiddlyRemember/templates/AnkiTags}}
<$transclude mode="block" />
</$set>

View File

@ -0,0 +1,13 @@
caption: RememberCz
condition: [<targetTiddler>!has[type]] [<targetTiddler>type[text/vnd.tiddlywiki]]
created: 20200517155905263
description: Remember Cloze Deletion
icon: $:/plugins/sobjornstad/TiddlyRemember/icons/lightbulb-black.svg
list-after: $:/plugins/sobjornstad/TiddlyRemember/toolbar/rememberq
modified: 20200517162124307
shortcuts: ((remembercz))
tags: $:/tags/EditorToolbar
title: $:/plugins/sobjornstad/TiddlyRemember/toolbar/remembercz
type: text/vnd.tiddlywiki
<$macrocall $name=tr-insert-note snippet={{$:/config/TiddlyRemember/snippets/remembercz}}>

View File

@ -0,0 +1,12 @@
caption: RememberQ
condition: [<targetTiddler>!has[type]] [<targetTiddler>type[text/vnd.tiddlywiki]]
created: 20161118171456554
description: Remember Question/Answer
icon: $:/plugins/sobjornstad/TiddlyRemember/icons/lightbulb-white.svg
modified: 20200517161709608
shortcuts: ((rememberq))
tags: $:/tags/EditorToolbar
title: $:/plugins/sobjornstad/TiddlyRemember/toolbar/rememberq
type: text/vnd.tiddlywiki
<$macrocall $name=tr-insert-note snippet={{$:/config/TiddlyRemember/snippets/rememberq}}>

View File

@ -2,7 +2,7 @@
"title": "$:/plugins/sobjornstad/TiddlyRemember",
"description": "TiddlyRemember: Embed Anki notes in your TiddlyWiki",
"author": "Soren Bjornstad",
"version": "1.2.2",
"version": "1.3.4",
"core-version": ">=5.1.21",
"source": "https://github.com/sobjornstad/TiddlyRemember",
"list": "readme license",

Some files were not shown because too many files have changed in this diff Show More