Jujutsu/git integration
The editor now has a button to commit changes to a Jujutsu repository, which is automatically created if it's not present. A git remote is automatically set up using the URL template in settings.py. I use this with Forgejo's create-on-push feature to automatically create repositories on my Forgejo instance.
This commit is contained in:
parent
500eb38774
commit
d474a394f5
13 changed files with 913 additions and 376 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -5,3 +5,4 @@ public/
|
|||
secret_key.txt
|
||||
think_data/
|
||||
Makefile
|
||||
thinkserver/settings.py
|
||||
|
|
|
@ -127,3 +127,10 @@ class RunCommandForm(forms.ModelForm):
|
|||
class Meta:
|
||||
model = Think
|
||||
fields = []
|
||||
|
||||
class GitCommitForm(forms.ModelForm):
|
||||
message = forms.CharField()
|
||||
|
||||
class Meta:
|
||||
model = Think
|
||||
fields = []
|
||||
|
|
85
thinks/jujutsu.py
Normal file
85
thinks/jujutsu.py
Normal file
|
@ -0,0 +1,85 @@
|
|||
import functools
|
||||
import subprocess
|
||||
from django.conf import settings
|
||||
|
||||
def ensure_jj(fn):
|
||||
@functools.wraps(fn)
|
||||
def ofn(self,*args,**kwargs):
|
||||
self.init_jj()
|
||||
return fn(self,*args,**kwargs)
|
||||
|
||||
return ofn
|
||||
|
||||
class JJController:
|
||||
def __init__(self, think):
|
||||
self.think = think
|
||||
self.root = think.root
|
||||
|
||||
def run(self, cmd, **kwargs):
|
||||
print("Run command",cmd)
|
||||
res = subprocess.run(
|
||||
cmd,
|
||||
cwd=self.root,
|
||||
encoding='utf8',
|
||||
capture_output=True,
|
||||
**kwargs
|
||||
)
|
||||
return res
|
||||
|
||||
def init_jj(self):
|
||||
print("Init jj")
|
||||
if not (self.root / '.jj').exists():
|
||||
self.run(['jj','git','init'])
|
||||
self.ignore_paths(['.make.*'])
|
||||
git_url = settings.GIT_REPO_URL_TEMPLATE.format(name=self.think.slug)
|
||||
self.run(['jj','git','remote','add','origin', git_url])
|
||||
|
||||
@ensure_jj
|
||||
def status(self):
|
||||
res = self.run(['jj','st'])
|
||||
|
||||
return res.stdout
|
||||
|
||||
def clean_paths(self, paths):
|
||||
paths = [self.root / p for p in paths]
|
||||
return [str(p.relative_to(self.root)) for p in paths if p.is_relative_to(self.root)]
|
||||
|
||||
@ensure_jj
|
||||
def ignore_paths(self, paths):
|
||||
paths = self.clean_paths(paths)
|
||||
gitignore = self.root / '.gitignore'
|
||||
if len(paths) == 0:
|
||||
return
|
||||
|
||||
if gitignore.exists():
|
||||
with open(gitignore) as f:
|
||||
ignored = f.read().strip().split('\n')
|
||||
ignored += [p for p in paths if p not in ignored]
|
||||
else:
|
||||
ignored = paths
|
||||
|
||||
with open(gitignore, 'w') as f:
|
||||
f.write('\n'.join(ignored))
|
||||
|
||||
@ensure_jj
|
||||
def remove_paths(self, paths):
|
||||
paths = self.clean_paths(paths)
|
||||
if len(paths) == 0:
|
||||
return
|
||||
|
||||
return self.run(['git','rm'] + paths)
|
||||
|
||||
@ensure_jj
|
||||
def add_paths(self, paths):
|
||||
paths = self.clean_paths(paths)
|
||||
if len(paths) == 0:
|
||||
return
|
||||
|
||||
return self.run(['git','add'] + paths)
|
||||
|
||||
@ensure_jj
|
||||
def commit(self, message):
|
||||
res = self.run(['jj','describe','--stdin','--no-edit'], input=message)
|
||||
if res.returncode == 0:
|
||||
self.run(['jj','new'])
|
||||
return res
|
|
@ -3,6 +3,8 @@ from django.db import models
|
|||
from django.urls import reverse
|
||||
from django.utils.timezone import datetime, make_aware
|
||||
|
||||
from .jujutsu import JJController
|
||||
|
||||
THINKS_DIR = settings.THINKS_DIR
|
||||
|
||||
# Create your models here.
|
||||
|
@ -21,6 +23,13 @@ class Think(models.Model):
|
|||
def root(self):
|
||||
return (THINKS_DIR / self.slug).resolve()
|
||||
|
||||
@property
|
||||
def jj_controller(self):
|
||||
return JJController(self)
|
||||
|
||||
def has_jj(self):
|
||||
return (self.root / '.jj').exists()
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('think', kwargs={'slug': self.slug})
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ import './code-editor.mjs';
|
|||
|
||||
export default async function init_app() {
|
||||
const flags = JSON.parse(document.getElementById('think-editor-data').textContent);
|
||||
const packages = await (await fetch('https://elm-package-list.think.somethingorotherwhatever.com/elm-packages.json')).json();
|
||||
flags.elm_packages = packages;
|
||||
flags.csrf_token = document.getElementById('csrftoken')?.textContent || '';
|
||||
const app = Elm.App.init({node: document.body, flags});
|
||||
|
||||
|
@ -16,4 +18,9 @@ export default async function init_app() {
|
|||
},10);
|
||||
}
|
||||
})
|
||||
|
||||
app.ports.show_modal.subscribe(id => {
|
||||
console.log(id);
|
||||
document.getElementById(id).showModal()
|
||||
})
|
||||
}
|
|
@ -1,9 +1,24 @@
|
|||
:root {
|
||||
--spacing: 1em;
|
||||
--quarter-spacing: calc(0.25 * var(--spacing));
|
||||
--half-spacing: calc(0.5 * var(--spacing));
|
||||
--double-spacing: calc(2 * var(--spacing));
|
||||
|
||||
--radius: 0.2em;
|
||||
|
||||
--editor-size: 50%;
|
||||
|
||||
--background: hsl(70,100%,95%);
|
||||
--color: black;
|
||||
--button-bg: #ddd;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
--background: hsl(70,100%,8%);
|
||||
--color: white;
|
||||
--button-bg: #333;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
|
@ -20,6 +35,9 @@ body {
|
|||
margin: 0;
|
||||
padding: var(--half-spacing);
|
||||
|
||||
background: var(--background);
|
||||
color: var(--color);
|
||||
|
||||
& > header {
|
||||
padding: var(--spacing);
|
||||
& h1 {
|
||||
|
@ -29,12 +47,6 @@ body {
|
|||
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
& #think-controls {
|
||||
|
@ -53,6 +65,56 @@ header {
|
|||
}
|
||||
|
||||
|
||||
button {
|
||||
border: thin solid currentColor;
|
||||
--button-background: var(--button-bg);
|
||||
--highlight: white;
|
||||
--highlight-amount: 0%;
|
||||
--darken: black;
|
||||
--darken-amount: 0%;
|
||||
background-color:
|
||||
color-mix(in oklab,
|
||||
color-mix(in oklab,
|
||||
var(--button-background),
|
||||
var(--darken) var(--darken-amount)
|
||||
),
|
||||
var(--highlight) var(--highlight-amount)
|
||||
);
|
||||
|
||||
&:focus {
|
||||
--highlight-amount: 40%;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
--highlight-amount: 60%;
|
||||
}
|
||||
|
||||
&:active {
|
||||
--highlight-amount: 0%;
|
||||
--darken-amount: 10%;
|
||||
}
|
||||
}
|
||||
|
||||
button[value="cancel"] {
|
||||
--button-background: color-mix(in oklab, var(--button-bg), var(--background) 50%);
|
||||
}
|
||||
|
||||
input:not([type="hidden"]) ~ button {
|
||||
border-radius: 0 var(--radius) var(--radius) 0;
|
||||
border-left: none;
|
||||
border-left: none;
|
||||
}
|
||||
|
||||
input {
|
||||
border: thin solid currentColor;
|
||||
}
|
||||
|
||||
|
||||
.field {
|
||||
display: flex;
|
||||
gap: var(--quarter-spacing);
|
||||
}
|
||||
|
||||
.think-editor {
|
||||
display: grid;
|
||||
gap: var(--spacing);
|
||||
|
@ -65,6 +127,12 @@ header {
|
|||
/ var(--col-1-width) var(--editor-size) var(--preview-size)
|
||||
;
|
||||
|
||||
|
||||
& > * {
|
||||
border: thin solid currentColor;
|
||||
padding: var(--quarter-spacing);
|
||||
}
|
||||
|
||||
&:has(#main-nav[open], #log[open]) {
|
||||
--col-1-width: 20em;
|
||||
}
|
||||
|
@ -83,7 +151,7 @@ header {
|
|||
}
|
||||
|
||||
& summary {
|
||||
background: #eee;
|
||||
background: color-mix(in oklab, var(--background), var(--color) 10%);
|
||||
}
|
||||
|
||||
& > #main-nav > nav {
|
||||
|
@ -120,6 +188,17 @@ header {
|
|||
align-items: start;
|
||||
}
|
||||
|
||||
& #jj-buttons {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
gap: var(--spacing);
|
||||
margin: 0;
|
||||
|
||||
& #start-commit-button {
|
||||
width: 10em;
|
||||
}
|
||||
}
|
||||
|
||||
& #make-log {
|
||||
& > pre {
|
||||
max-width: 20em;
|
||||
|
@ -129,16 +208,19 @@ header {
|
|||
}
|
||||
|
||||
& #editor {
|
||||
overflow: hidden;
|
||||
overflow-x: hidden;
|
||||
flex-grow: 1;
|
||||
flex-basis: var(--editor-size);
|
||||
max-height: 85vh;
|
||||
grid-area: editor;
|
||||
|
||||
& #editor-controls {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
display: flex;
|
||||
gap: var(--spacing);
|
||||
justify-content: space-between;
|
||||
border-bottom: thin solid currentColor;
|
||||
|
||||
& > details {
|
||||
text-align: right;
|
||||
|
@ -151,6 +233,8 @@ header {
|
|||
margin: var(--half-spacing) 0;
|
||||
}
|
||||
}
|
||||
background: var(--background);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
& #code-editor {
|
||||
|
@ -189,13 +273,43 @@ header {
|
|||
|
||||
#file-form {
|
||||
overflow: auto;
|
||||
max-height: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
dialog h2:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#jj-status {
|
||||
max-height: 10em;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
dialog textarea {
|
||||
width: 100%;
|
||||
height: 6em;
|
||||
resize-x: none;
|
||||
}
|
||||
|
||||
dialog p:last-child {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
gap: var(--spacing);
|
||||
}
|
||||
|
||||
@media (max-width: 100ch) {
|
||||
html {
|
||||
font-size: min(3vw, 16px);
|
||||
}
|
||||
body {
|
||||
grid-template-columns: calc(100svw - var(--spacing));
|
||||
}
|
||||
|
||||
#editor-size {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.think-editor {
|
||||
overflow: visible;
|
||||
grid-template:
|
||||
|
@ -205,6 +319,8 @@ header {
|
|||
"preview"
|
||||
;
|
||||
|
||||
padding-left: var(--double-spacing);
|
||||
|
||||
& > * ~ * {
|
||||
border-top: medium solid #888;
|
||||
margin-top: var(--spacing);
|
||||
|
@ -219,12 +335,13 @@ header {
|
|||
}
|
||||
|
||||
& #file-tree {
|
||||
max-height: 7em;
|
||||
max-height: 30svh;
|
||||
}
|
||||
}
|
||||
|
||||
& #editor {
|
||||
overflow: auto;
|
||||
max-height: revert;
|
||||
& #code-editor {
|
||||
max-width: none;
|
||||
}
|
||||
|
|
|
@ -5536,6 +5536,8 @@ var $author$project$App$Model = function (show_preview) {
|
|||
return function (log) {
|
||||
return function (command_to_run) {
|
||||
return function (selected_package) {
|
||||
return function (jj_status) {
|
||||
return function (commit_message) {
|
||||
return function (csrf_token) {
|
||||
return function (preview_url) {
|
||||
return function (slug) {
|
||||
|
@ -5544,7 +5546,9 @@ var $author$project$App$Model = function (show_preview) {
|
|||
return function (file_content) {
|
||||
return function (is_dir) {
|
||||
return function (elm_packages) {
|
||||
return {command_to_run: command_to_run, content_changed: content_changed, csrf_token: csrf_token, editor_size: editor_size, elm_packages: elm_packages, file_content: file_content, file_path: file_path, files: files, is_dir: is_dir, log: log, preview_url: preview_url, selected_package: selected_package, show_log: show_log, show_preview: show_preview, slug: slug};
|
||||
return {command_to_run: command_to_run, commit_message: commit_message, content_changed: content_changed, csrf_token: csrf_token, editor_size: editor_size, elm_packages: elm_packages, file_content: file_content, file_path: file_path, files: files, is_dir: is_dir, jj_status: jj_status, log: log, preview_url: preview_url, selected_package: selected_package, show_log: show_log, show_preview: show_preview, slug: slug};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -5594,9 +5598,8 @@ var $author$project$App$decode_file_info = A4(
|
|||
A2($elm$json$Json$Decode$field, 'is_dir', $elm$json$Json$Decode$bool),
|
||||
A2($elm$json$Json$Decode$field, 'path', $elm$json$Json$Decode$string));
|
||||
var $author$project$App$NotLoaded = {$: 'NotLoaded'};
|
||||
var $author$project$App$init_model = {command_to_run: '', content_changed: false, csrf_token: '', editor_size: 0.5, elm_packages: _List_Nil, file_content: 'The editor has not loaded successfully.', file_path: 'Oops!', files: _List_Nil, is_dir: false, log: $author$project$App$NotLoaded, preview_url: '', selected_package: '', show_log: false, show_preview: true, slug: 'not-a-real-thing'};
|
||||
var $author$project$App$init_model = {command_to_run: '', commit_message: '', content_changed: false, csrf_token: '', editor_size: 0.5, elm_packages: _List_Nil, file_content: 'The editor has not loaded successfully.', file_path: 'Oops!', files: _List_Nil, is_dir: false, jj_status: '', log: $author$project$App$NotLoaded, preview_url: '', selected_package: '', show_log: false, show_preview: true, slug: 'not-a-real-thing'};
|
||||
var $elm$json$Json$Decode$list = _Json_decodeList;
|
||||
var $elm$core$Debug$log = _Debug_log;
|
||||
var $elm$json$Json$Decode$oneOf = _Json_oneOf;
|
||||
var $elm$core$Result$withDefault = F2(
|
||||
function (def, result) {
|
||||
|
@ -5646,11 +5649,8 @@ var $author$project$App$load_flags = A2(
|
|||
$elm_community$json_extra$Json$Decode$Extra$andMap,
|
||||
A2($elm$json$Json$Decode$field, 'csrf_token', $elm$json$Json$Decode$string),
|
||||
$elm$json$Json$Decode$succeed(
|
||||
A7($author$project$App$Model, $author$project$App$init_model.show_preview, $author$project$App$init_model.show_log, $author$project$App$init_model.editor_size, $author$project$App$init_model.content_changed, $author$project$App$init_model.log, $author$project$App$init_model.command_to_run, $author$project$App$init_model.selected_package))))))))))),
|
||||
A2(
|
||||
$elm$core$Basics$composeR,
|
||||
$elm$core$Result$withDefault($author$project$App$init_model),
|
||||
$elm$core$Debug$log('model')));
|
||||
A9($author$project$App$Model, $author$project$App$init_model.show_preview, $author$project$App$init_model.show_log, $author$project$App$init_model.editor_size, $author$project$App$init_model.content_changed, $author$project$App$init_model.log, $author$project$App$init_model.command_to_run, $author$project$App$init_model.selected_package, $author$project$App$init_model.jj_status, $author$project$App$init_model.commit_message))))))))))),
|
||||
$elm$core$Result$withDefault($author$project$App$init_model));
|
||||
var $author$project$App$SetLog = function (a) {
|
||||
return {$: 'SetLog', a: a};
|
||||
};
|
||||
|
@ -6453,6 +6453,9 @@ var $author$project$App$FileUploaded = function (a) {
|
|||
var $author$project$App$HttpError = function (a) {
|
||||
return {$: 'HttpError', a: a};
|
||||
};
|
||||
var $author$project$App$JJCommitResponse = function (a) {
|
||||
return {$: 'JJCommitResponse', a: a};
|
||||
};
|
||||
var $author$project$App$MakeError = function (a) {
|
||||
return {$: 'MakeError', a: a};
|
||||
};
|
||||
|
@ -6465,6 +6468,9 @@ var $author$project$App$ReceiveCommandResult = function (a) {
|
|||
return {$: 'ReceiveCommandResult', a: a};
|
||||
};
|
||||
var $author$project$App$ReloadPreview = {$: 'ReloadPreview'};
|
||||
var $author$project$App$RunCommand = function (a) {
|
||||
return {$: 'RunCommand', a: a};
|
||||
};
|
||||
var $author$project$App$SaveContent = function (a) {
|
||||
return {$: 'SaveContent', a: a};
|
||||
};
|
||||
|
@ -6475,6 +6481,7 @@ var $author$project$App$UploadFile = F2(
|
|||
function (a, b) {
|
||||
return {$: 'UploadFile', a: a, b: b};
|
||||
});
|
||||
var $elm$core$Platform$Cmd$batch = _Platform_batch;
|
||||
var $author$project$App$decode_command_response = $elm$json$Json$Decode$oneOf(
|
||||
_List_fromArray(
|
||||
[
|
||||
|
@ -6535,6 +6542,24 @@ var $elm$http$Http$expectWhatever = function (toMsg) {
|
|||
return $elm$core$Result$Ok(_Utils_Tuple0);
|
||||
}));
|
||||
};
|
||||
var $author$project$App$ReceiveJJStatus = function (a) {
|
||||
return {$: 'ReceiveJJStatus', a: a};
|
||||
};
|
||||
var $author$project$App$fetch_jj_status = $elm$http$Http$get(
|
||||
{
|
||||
expect: A2(
|
||||
$elm$http$Http$expectJson,
|
||||
function (r) {
|
||||
if (r.$ === 'Ok') {
|
||||
var response = r.a;
|
||||
return $author$project$App$ReceiveJJStatus(response);
|
||||
} else {
|
||||
return $author$project$App$NoOp;
|
||||
}
|
||||
},
|
||||
A2($elm$json$Json$Decode$field, 'status', $elm$json$Json$Decode$string)),
|
||||
url: 'jj/status'
|
||||
});
|
||||
var $elm$url$Url$Builder$toQueryPair = function (_v0) {
|
||||
var key = _v0.a;
|
||||
var value = _v0.b;
|
||||
|
@ -6599,7 +6624,6 @@ var $elm$time$Time$Posix = function (a) {
|
|||
var $elm$time$Time$millisToPosix = $elm$time$Time$Posix;
|
||||
var $elm$file$File$name = _File_name;
|
||||
var $elm$core$Basics$neq = _Utils_notEqual;
|
||||
var $elm$core$Platform$Cmd$batch = _Platform_batch;
|
||||
var $elm$core$Platform$Cmd$none = $elm$core$Platform$Cmd$batch(_List_Nil);
|
||||
var $author$project$App$nocmd = function (model) {
|
||||
return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none);
|
||||
|
@ -6618,15 +6642,16 @@ var $author$project$App$set_log = F2(
|
|||
function (log, model) {
|
||||
return _Utils_update(
|
||||
model,
|
||||
{
|
||||
log: log,
|
||||
show_log: A2($elm$core$Debug$log, 'show log', true)
|
||||
});
|
||||
{log: log, show_log: true});
|
||||
});
|
||||
var $elm$json$Json$Encode$string = _Json_wrap;
|
||||
var $author$project$App$show_modal = _Platform_outgoingPort('show_modal', $elm$json$Json$Encode$string);
|
||||
var $elm$http$Http$stringPart = _Http_pair;
|
||||
var $elm$file$File$toString = _File_toString;
|
||||
var $author$project$App$update = F2(
|
||||
function (msg, model) {
|
||||
update:
|
||||
while (true) {
|
||||
switch (msg.$) {
|
||||
case 'SetFileContent':
|
||||
var content = msg.a;
|
||||
|
@ -6732,9 +6757,7 @@ var $author$project$App$update = F2(
|
|||
return $author$project$App$nocmd(
|
||||
_Utils_update(
|
||||
model,
|
||||
{
|
||||
show_log: A2($elm$core$Debug$log, 'toggle', show)
|
||||
}));
|
||||
{show_log: show}));
|
||||
case 'DropFiles':
|
||||
var action = msg.a;
|
||||
var files = msg.b;
|
||||
|
@ -6778,8 +6801,7 @@ var $author$project$App$update = F2(
|
|||
])),
|
||||
expect: $elm$http$Http$expectWhatever(
|
||||
function (r) {
|
||||
var _v5 = A2($elm$core$Debug$log, 'uploaded file', r);
|
||||
if (_v5.$ === 'Ok') {
|
||||
if (r.$ === 'Ok') {
|
||||
return $author$project$App$FileUploaded(file);
|
||||
} else {
|
||||
return $author$project$App$NoOp;
|
||||
|
@ -6848,9 +6870,80 @@ var $author$project$App$update = F2(
|
|||
$author$project$App$HttpError(err),
|
||||
model));
|
||||
}
|
||||
case 'ShowCommitModal':
|
||||
return _Utils_Tuple2(
|
||||
model,
|
||||
$elm$core$Platform$Cmd$batch(
|
||||
_List_fromArray(
|
||||
[
|
||||
$author$project$App$fetch_jj_status,
|
||||
$author$project$App$show_modal('commit-modal')
|
||||
])));
|
||||
case 'SetCommitMessage':
|
||||
var message = msg.a;
|
||||
return $author$project$App$nocmd(
|
||||
_Utils_update(
|
||||
model,
|
||||
{commit_message: message}));
|
||||
case 'ReceiveJJStatus':
|
||||
var status = msg.a;
|
||||
return $author$project$App$nocmd(
|
||||
_Utils_update(
|
||||
model,
|
||||
{commit_message: '', jj_status: status}));
|
||||
case 'JJCommit':
|
||||
return _Utils_Tuple2(
|
||||
model,
|
||||
$elm$http$Http$post(
|
||||
{
|
||||
body: $elm$http$Http$multipartBody(
|
||||
_List_fromArray(
|
||||
[
|
||||
A2($elm$http$Http$stringPart, 'message', model.commit_message),
|
||||
A2($elm$http$Http$stringPart, 'csrfmiddlewaretoken', model.csrf_token)
|
||||
])),
|
||||
expect: A2($elm$http$Http$expectJson, $author$project$App$JJCommitResponse, $author$project$App$decode_command_response),
|
||||
url: 'jj/commit'
|
||||
}));
|
||||
case 'JJCommitResponse':
|
||||
var response = msg.a;
|
||||
if (response.$ === 'Ok') {
|
||||
if (response.a.$ === 'Ok') {
|
||||
var res = response.a.a;
|
||||
return $author$project$App$nocmd(
|
||||
_Utils_update(
|
||||
model,
|
||||
{
|
||||
log: A2($author$project$App$MakeResult, res.stdout, res.stderr)
|
||||
}));
|
||||
} else {
|
||||
var errmsg = response.a.a;
|
||||
return $author$project$App$nocmd(
|
||||
_Utils_update(
|
||||
model,
|
||||
{
|
||||
log: $author$project$App$MakeError(errmsg)
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
var err = response.a;
|
||||
return $author$project$App$nocmd(
|
||||
_Utils_update(
|
||||
model,
|
||||
{
|
||||
log: $author$project$App$HttpError(err)
|
||||
}));
|
||||
}
|
||||
case 'JJPush':
|
||||
var $temp$msg = $author$project$App$RunCommand('jj bookmark set main -r @- && jj git push -b main --allow-new'),
|
||||
$temp$model = model;
|
||||
msg = $temp$msg;
|
||||
model = $temp$model;
|
||||
continue update;
|
||||
default:
|
||||
return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none);
|
||||
}
|
||||
}
|
||||
});
|
||||
var $elm$json$Json$Decode$value = _Json_decodeValue;
|
||||
var $elm$virtual_dom$VirtualDom$attribute = F2(
|
||||
|
@ -6861,7 +6954,6 @@ var $elm$virtual_dom$VirtualDom$attribute = F2(
|
|||
_VirtualDom_noJavaScriptOrHtmlUri(value));
|
||||
});
|
||||
var $elm$html$Html$Attributes$attribute = $elm$virtual_dom$VirtualDom$attribute;
|
||||
var $elm$json$Json$Encode$string = _Json_wrap;
|
||||
var $elm$html$Html$Attributes$stringProperty = F2(
|
||||
function (key, string) {
|
||||
return A2(
|
||||
|
@ -6895,6 +6987,191 @@ var $elm$html$Html$Attributes$classList = function (classes) {
|
|||
$elm$core$Tuple$first,
|
||||
A2($elm$core$List$filter, $elm$core$Tuple$second, classes))));
|
||||
};
|
||||
var $author$project$App$JJCommit = {$: 'JJCommit'};
|
||||
var $author$project$App$SetCommitMessage = function (a) {
|
||||
return {$: 'SetCommitMessage', a: a};
|
||||
};
|
||||
var $elm$json$Json$Decode$at = F2(
|
||||
function (fields, decoder) {
|
||||
return A3($elm$core$List$foldr, $elm$json$Json$Decode$field, decoder, fields);
|
||||
});
|
||||
var $elm$html$Html$button = _VirtualDom_node('button');
|
||||
var $elm$html$Html$Attributes$for = $elm$html$Html$Attributes$stringProperty('htmlFor');
|
||||
var $elm$html$Html$form = _VirtualDom_node('form');
|
||||
var $elm$html$Html$h2 = _VirtualDom_node('h2');
|
||||
var $elm$html$Html$Attributes$id = $elm$html$Html$Attributes$stringProperty('id');
|
||||
var $elm$html$Html$label = _VirtualDom_node('label');
|
||||
var $elm$html$Html$Attributes$method = $elm$html$Html$Attributes$stringProperty('method');
|
||||
var $elm$virtual_dom$VirtualDom$node = function (tag) {
|
||||
return _VirtualDom_node(
|
||||
_VirtualDom_noScript(tag));
|
||||
};
|
||||
var $elm$html$Html$node = $elm$virtual_dom$VirtualDom$node;
|
||||
var $elm$virtual_dom$VirtualDom$Normal = function (a) {
|
||||
return {$: 'Normal', a: a};
|
||||
};
|
||||
var $elm$virtual_dom$VirtualDom$on = _VirtualDom_on;
|
||||
var $elm$html$Html$Events$on = F2(
|
||||
function (event, decoder) {
|
||||
return A2(
|
||||
$elm$virtual_dom$VirtualDom$on,
|
||||
event,
|
||||
$elm$virtual_dom$VirtualDom$Normal(decoder));
|
||||
});
|
||||
var $elm$html$Html$Events$alwaysStop = function (x) {
|
||||
return _Utils_Tuple2(x, true);
|
||||
};
|
||||
var $elm$virtual_dom$VirtualDom$MayStopPropagation = function (a) {
|
||||
return {$: 'MayStopPropagation', a: a};
|
||||
};
|
||||
var $elm$html$Html$Events$stopPropagationOn = F2(
|
||||
function (event, decoder) {
|
||||
return A2(
|
||||
$elm$virtual_dom$VirtualDom$on,
|
||||
event,
|
||||
$elm$virtual_dom$VirtualDom$MayStopPropagation(decoder));
|
||||
});
|
||||
var $elm$html$Html$Events$targetValue = A2(
|
||||
$elm$json$Json$Decode$at,
|
||||
_List_fromArray(
|
||||
['target', 'value']),
|
||||
$elm$json$Json$Decode$string);
|
||||
var $elm$html$Html$Events$onInput = function (tagger) {
|
||||
return A2(
|
||||
$elm$html$Html$Events$stopPropagationOn,
|
||||
'input',
|
||||
A2(
|
||||
$elm$json$Json$Decode$map,
|
||||
$elm$html$Html$Events$alwaysStop,
|
||||
A2($elm$json$Json$Decode$map, tagger, $elm$html$Html$Events$targetValue)));
|
||||
};
|
||||
var $elm$html$Html$p = _VirtualDom_node('p');
|
||||
var $elm$html$Html$pre = _VirtualDom_node('pre');
|
||||
var $elm$html$Html$td = _VirtualDom_node('td');
|
||||
var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text;
|
||||
var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text;
|
||||
var $elm$html$Html$textarea = _VirtualDom_node('textarea');
|
||||
var $elm$html$Html$tr = _VirtualDom_node('tr');
|
||||
var $elm$html$Html$Attributes$type_ = $elm$html$Html$Attributes$stringProperty('type');
|
||||
var $elm$html$Html$Attributes$value = $elm$html$Html$Attributes$stringProperty('value');
|
||||
var $author$project$App$commit_modal = function (model) {
|
||||
var view_status = function (status) {
|
||||
return A2(
|
||||
$elm$html$Html$tr,
|
||||
_List_Nil,
|
||||
_List_fromArray(
|
||||
[
|
||||
A2(
|
||||
$elm$html$Html$td,
|
||||
_List_Nil,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text(status.path)
|
||||
]))
|
||||
]));
|
||||
};
|
||||
return A3(
|
||||
$elm$html$Html$node,
|
||||
'dialog',
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$id('commit-modal')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
A2(
|
||||
$elm$html$Html$h2,
|
||||
_List_Nil,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text('Commit')
|
||||
])),
|
||||
A2(
|
||||
$elm$html$Html$pre,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$id('jj-status')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text(model.jj_status)
|
||||
])),
|
||||
A2(
|
||||
$elm$html$Html$form,
|
||||
_List_fromArray(
|
||||
[
|
||||
A2(
|
||||
$elm$html$Html$Events$on,
|
||||
'submit',
|
||||
A2(
|
||||
$elm$json$Json$Decode$map,
|
||||
function (v) {
|
||||
if (v === 'cancel') {
|
||||
return $author$project$App$NoOp;
|
||||
} else {
|
||||
return $author$project$App$JJCommit;
|
||||
}
|
||||
},
|
||||
A2(
|
||||
$elm$json$Json$Decode$at,
|
||||
_List_fromArray(
|
||||
['submitter', 'value']),
|
||||
$elm$json$Json$Decode$string))),
|
||||
$elm$html$Html$Attributes$method('dialog')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
A2(
|
||||
$elm$html$Html$label,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$for('commit-message')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text('Commit message')
|
||||
])),
|
||||
A2(
|
||||
$elm$html$Html$textarea,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$value(model.commit_message),
|
||||
$elm$html$Html$Events$onInput($author$project$App$SetCommitMessage)
|
||||
]),
|
||||
_List_Nil),
|
||||
A2(
|
||||
$elm$html$Html$p,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$class('modal-controls')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
A2(
|
||||
$elm$html$Html$button,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$value('cancel'),
|
||||
A2($elm$html$Html$Attributes$attribute, 'formmethod', 'dialog')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text('Cancel')
|
||||
])),
|
||||
A2(
|
||||
$elm$html$Html$button,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$type_('submit')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text('Commit')
|
||||
]))
|
||||
]))
|
||||
]))
|
||||
]));
|
||||
};
|
||||
var $author$project$App$Content = {$: 'Content'};
|
||||
var $author$project$App$DropFiles = F2(
|
||||
function (a, b) {
|
||||
|
@ -6906,18 +7183,10 @@ var $elm$html$Html$Attributes$action = function (uri) {
|
|||
'action',
|
||||
_VirtualDom_noJavaScriptUri(uri));
|
||||
};
|
||||
var $elm$json$Json$Decode$at = F2(
|
||||
function (fields, decoder) {
|
||||
return A3($elm$core$List$foldr, $elm$json$Json$Decode$field, decoder, fields);
|
||||
});
|
||||
var $elm$html$Html$button = _VirtualDom_node('button');
|
||||
var $elm$file$File$decoder = _File_decoder;
|
||||
var $elm$html$Html$details = _VirtualDom_node('details');
|
||||
var $elm$html$Html$form = _VirtualDom_node('form');
|
||||
var $elm$html$Html$input = _VirtualDom_node('input');
|
||||
var $elm$html$Html$Attributes$name = $elm$html$Html$Attributes$stringProperty('name');
|
||||
var $elm$html$Html$Attributes$type_ = $elm$html$Html$Attributes$stringProperty('type');
|
||||
var $elm$html$Html$Attributes$value = $elm$html$Html$Attributes$stringProperty('value');
|
||||
var $author$project$App$form = F3(
|
||||
function (model, attrs, children) {
|
||||
return A2(
|
||||
|
@ -6938,25 +7207,7 @@ var $author$project$App$form = F3(
|
|||
_List_Nil)
|
||||
])));
|
||||
});
|
||||
var $elm$html$Html$Attributes$id = $elm$html$Html$Attributes$stringProperty('id');
|
||||
var $elm$html$Html$Attributes$method = $elm$html$Html$Attributes$stringProperty('method');
|
||||
var $elm$html$Html$nav = _VirtualDom_node('nav');
|
||||
var $elm$virtual_dom$VirtualDom$node = function (tag) {
|
||||
return _VirtualDom_node(
|
||||
_VirtualDom_noScript(tag));
|
||||
};
|
||||
var $elm$html$Html$node = $elm$virtual_dom$VirtualDom$node;
|
||||
var $elm$virtual_dom$VirtualDom$Normal = function (a) {
|
||||
return {$: 'Normal', a: a};
|
||||
};
|
||||
var $elm$virtual_dom$VirtualDom$on = _VirtualDom_on;
|
||||
var $elm$html$Html$Events$on = F2(
|
||||
function (event, decoder) {
|
||||
return A2(
|
||||
$elm$virtual_dom$VirtualDom$on,
|
||||
event,
|
||||
$elm$virtual_dom$VirtualDom$Normal(decoder));
|
||||
});
|
||||
var $elm$virtual_dom$VirtualDom$MayPreventDefault = function (a) {
|
||||
return {$: 'MayPreventDefault', a: a};
|
||||
};
|
||||
|
@ -6970,8 +7221,6 @@ var $elm$html$Html$Events$preventDefaultOn = F2(
|
|||
var $elm$html$Html$section = _VirtualDom_node('section');
|
||||
var $elm$html$Html$span = _VirtualDom_node('span');
|
||||
var $elm$html$Html$summary = _VirtualDom_node('summary');
|
||||
var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text;
|
||||
var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text;
|
||||
var $author$project$App$editor_pane = function (model) {
|
||||
return A2(
|
||||
$elm$html$Html$section,
|
||||
|
@ -7185,7 +7434,6 @@ var $author$project$App$as_percentage = function (amount) {
|
|||
};
|
||||
var $elm$html$Html$datalist = _VirtualDom_node('datalist');
|
||||
var $elm$json$Json$Decode$float = _Json_decodeFloat;
|
||||
var $elm$html$Html$Attributes$for = $elm$html$Html$Attributes$stringProperty('htmlFor');
|
||||
var $elm$html$Html$h1 = _VirtualDom_node('h1');
|
||||
var $elm$html$Html$header = _VirtualDom_node('header');
|
||||
var $elm$html$Html$Attributes$href = function (url) {
|
||||
|
@ -7194,7 +7442,6 @@ var $elm$html$Html$Attributes$href = function (url) {
|
|||
'href',
|
||||
_VirtualDom_noJavaScriptUri(url));
|
||||
};
|
||||
var $elm$html$Html$label = _VirtualDom_node('label');
|
||||
var $author$project$App$link = F2(
|
||||
function (url, text) {
|
||||
return A2(
|
||||
|
@ -7213,7 +7460,6 @@ var $elm$html$Html$Attributes$max = $elm$html$Html$Attributes$stringProperty('ma
|
|||
var $elm$html$Html$Attributes$min = $elm$html$Html$Attributes$stringProperty('min');
|
||||
var $elm$html$Html$option = _VirtualDom_node('option');
|
||||
var $elm$html$Html$output = _VirtualDom_node('output');
|
||||
var $elm$html$Html$p = _VirtualDom_node('p');
|
||||
var $elm$html$Html$Attributes$step = function (n) {
|
||||
return A2($elm$html$Html$Attributes$stringProperty, 'step', n);
|
||||
};
|
||||
|
@ -7224,7 +7470,7 @@ var $author$project$App$header = function (model) {
|
|||
_List_Nil,
|
||||
_List_fromArray(
|
||||
[
|
||||
A2($author$project$App$link, '/', 'thinks'),
|
||||
A2($author$project$App$link, '/', '← thinks'),
|
||||
A2(
|
||||
$elm$html$Html$h1,
|
||||
_List_Nil,
|
||||
|
@ -7254,6 +7500,15 @@ var $author$project$App$header = function (model) {
|
|||
A2($author$project$App$link, 'rename', 'Rename'),
|
||||
A2($author$project$App$link, 'delete', 'Delete'),
|
||||
A2($author$project$App$link, '/new/' + model.slug, 'Remix'),
|
||||
A2(
|
||||
$elm$html$Html$span,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$class('field'),
|
||||
$elm$html$Html$Attributes$id('editor-size')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
A2(
|
||||
$elm$html$Html$label,
|
||||
_List_fromArray(
|
||||
|
@ -7299,6 +7554,7 @@ var $author$project$App$header = function (model) {
|
|||
[
|
||||
$elm$html$Html$text(
|
||||
$author$project$App$as_percentage(model.editor_size))
|
||||
]))
|
||||
])),
|
||||
A2(
|
||||
$elm$html$Html$datalist,
|
||||
|
@ -7326,8 +7582,6 @@ var $elm$html$Html$dd = _VirtualDom_node('dd');
|
|||
var $elm$html$Html$div = _VirtualDom_node('div');
|
||||
var $elm$html$Html$dl = _VirtualDom_node('dl');
|
||||
var $elm$html$Html$dt = _VirtualDom_node('dt');
|
||||
var $elm$html$Html$h2 = _VirtualDom_node('h2');
|
||||
var $elm$html$Html$pre = _VirtualDom_node('pre');
|
||||
var $author$project$App$log_pane = function (model) {
|
||||
return A2(
|
||||
$elm$html$Html$details,
|
||||
|
@ -7529,15 +7783,14 @@ var $author$project$App$log_pane = function (model) {
|
|||
]));
|
||||
};
|
||||
var $elm$html$Html$main_ = _VirtualDom_node('main');
|
||||
var $author$project$App$RunCommand = function (a) {
|
||||
return {$: 'RunCommand', a: a};
|
||||
};
|
||||
var $author$project$App$JJPush = {$: 'JJPush'};
|
||||
var $author$project$App$SelectPackage = function (a) {
|
||||
return {$: 'SelectPackage', a: a};
|
||||
};
|
||||
var $author$project$App$SetCommand = function (a) {
|
||||
return {$: 'SetCommand', a: a};
|
||||
};
|
||||
var $author$project$App$ShowCommitModal = {$: 'ShowCommitModal'};
|
||||
var $author$project$App$Upload = {$: 'Upload'};
|
||||
var $elm$core$List$drop = F2(
|
||||
function (n, list) {
|
||||
|
@ -7562,32 +7815,11 @@ var $elm$core$List$drop = F2(
|
|||
});
|
||||
var $elm$html$Html$li = _VirtualDom_node('li');
|
||||
var $elm$core$Basics$not = _Basics_not;
|
||||
var $elm$html$Html$Events$alwaysStop = function (x) {
|
||||
return _Utils_Tuple2(x, true);
|
||||
};
|
||||
var $elm$virtual_dom$VirtualDom$MayStopPropagation = function (a) {
|
||||
return {$: 'MayStopPropagation', a: a};
|
||||
};
|
||||
var $elm$html$Html$Events$stopPropagationOn = F2(
|
||||
function (event, decoder) {
|
||||
var $elm$html$Html$Events$onClick = function (msg) {
|
||||
return A2(
|
||||
$elm$virtual_dom$VirtualDom$on,
|
||||
event,
|
||||
$elm$virtual_dom$VirtualDom$MayStopPropagation(decoder));
|
||||
});
|
||||
var $elm$html$Html$Events$targetValue = A2(
|
||||
$elm$json$Json$Decode$at,
|
||||
_List_fromArray(
|
||||
['target', 'value']),
|
||||
$elm$json$Json$Decode$string);
|
||||
var $elm$html$Html$Events$onInput = function (tagger) {
|
||||
return A2(
|
||||
$elm$html$Html$Events$stopPropagationOn,
|
||||
'input',
|
||||
A2(
|
||||
$elm$json$Json$Decode$map,
|
||||
$elm$html$Html$Events$alwaysStop,
|
||||
A2($elm$json$Json$Decode$map, tagger, $elm$html$Html$Events$targetValue)));
|
||||
$elm$html$Html$Events$on,
|
||||
'click',
|
||||
$elm$json$Json$Decode$succeed(msg));
|
||||
};
|
||||
var $elm$html$Html$Events$alwaysPreventDefault = function (msg) {
|
||||
return _Utils_Tuple2(msg, true);
|
||||
|
@ -7820,7 +8052,40 @@ var $author$project$App$main_nav = function (model) {
|
|||
[
|
||||
$elm$html$Html$text('Install')
|
||||
]))
|
||||
])) : $elm$html$Html$text('')
|
||||
])) : $elm$html$Html$text(''),
|
||||
A2(
|
||||
$elm$html$Html$p,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$id('jj-buttons')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
A2(
|
||||
$elm$html$Html$button,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$id('start-commit-button'),
|
||||
$elm$html$Html$Attributes$type_('button'),
|
||||
$elm$html$Html$Events$onClick($author$project$App$ShowCommitModal)
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text('Commit')
|
||||
])),
|
||||
A2(
|
||||
$elm$html$Html$button,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$id('jj-push-button'),
|
||||
$elm$html$Html$Attributes$type_('button'),
|
||||
$elm$html$Html$Events$onClick($author$project$App$JJPush)
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text('Push')
|
||||
]))
|
||||
]))
|
||||
]))
|
||||
]));
|
||||
};
|
||||
|
@ -7901,7 +8166,8 @@ var $author$project$App$view = function (model) {
|
|||
$author$project$App$log_pane(model),
|
||||
model.is_dir ? $elm$html$Html$text('') : $author$project$App$editor_pane(model),
|
||||
$author$project$App$preview_pane(model)
|
||||
]))
|
||||
])),
|
||||
$author$project$App$commit_modal(model)
|
||||
]),
|
||||
title: model.file_path + (' - ' + (model.slug + ' - Thinks'))
|
||||
};
|
||||
|
|
|
@ -2,6 +2,14 @@
|
|||
--spacing: 1em;
|
||||
--half-spacing: calc(0.5 * var(--spacing));
|
||||
--double-spacing: calc(2 * var(--spacing));
|
||||
|
||||
--background: hsl(70,100%,95%);
|
||||
--color: black;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
--background: hsl(70,100%,8%);
|
||||
--color: white;
|
||||
}
|
||||
|
||||
* {
|
||||
|
@ -9,6 +17,11 @@
|
|||
}
|
||||
|
||||
body {
|
||||
color-scheme: light dark;
|
||||
|
||||
background: var(--background);
|
||||
color: var(--color);
|
||||
|
||||
font-family: sans-serif;
|
||||
& > header {
|
||||
padding: var(--spacing);
|
||||
|
@ -97,6 +110,20 @@ body.index {
|
|||
max-width: 80ch;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
& .jj {
|
||||
writing-mode: vertical-lr;
|
||||
vertical-align: middle;
|
||||
font-size: 0.5em;
|
||||
color: white;
|
||||
background: black;
|
||||
padding: 0.2em;
|
||||
border-radius: 0.5em;
|
||||
|
||||
&.has {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,3 +267,4 @@ body.thing-editor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,15 +25,7 @@
|
|||
|
||||
<ul class="thinks-list">
|
||||
{% for think in recent_thinks %}
|
||||
<li class="think">
|
||||
<a href="{% url 'think' think.slug %}">{{think.slug}}</a>
|
||||
<time datetime="{{think.creation_time|date:"c"}}">{{think.creation_time}}</time>
|
||||
{% with readme=think.get_readme %}
|
||||
{% if readme %}
|
||||
<pre class="readme">{{readme|safe}}</pre>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</li>
|
||||
{% include "thinks/think_list_item.html" %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
|
@ -45,19 +37,11 @@
|
|||
<ul class="thinks-list">
|
||||
{% for category in categories %}
|
||||
<li>
|
||||
<details open>
|
||||
<details>
|
||||
<summary>{% if category.grouper %}{{category.grouper}}{% else %}Uncategorised{% endif %}</summary>
|
||||
<ul>
|
||||
{% for think in category.list %}
|
||||
<li class="think">
|
||||
<a href="{% url 'think' think.slug %}">{{think.slug}}</a>
|
||||
<time datetime="{{think.creation_time|date:"c"}}">{{think.creation_time}}</time>
|
||||
{% with readme=think.get_readme %}
|
||||
{% if readme %}
|
||||
<pre class="readme">{{readme|safe}}</pre>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</li>
|
||||
{% include "thinks/think_list_item.html" %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
|
|
10
thinks/templates/thinks/think_list_item.html
Normal file
10
thinks/templates/thinks/think_list_item.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
<li class="think">
|
||||
<a href="{% url 'think' think.slug %}">{{think.slug}}</a>
|
||||
<time datetime="{{think.creation_time|date:"c"}}">{{think.creation_time}}</time>
|
||||
<small class="jj {% if think.has_jj %}has{% endif %}">备份{% if think.has_jj %}✔{% else %}✗{% endif %}</small>
|
||||
{% with readme=think.get_readme %}
|
||||
{% if readme %}
|
||||
<pre class="readme">{{readme|safe}}</pre>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</li>
|
|
@ -13,6 +13,8 @@ urlpatterns = [
|
|||
path('think/<slug:slug>/delete-file', DeleteFileView.as_view(), name='delete_file'),
|
||||
path('think/<slug:slug>/run-command', RunCommandView.as_view(), name='run_command'),
|
||||
path('think/<slug:slug>/log', LogView.as_view(), name='log'),
|
||||
path('think/<slug:slug>/jj/status', JJStatusView.as_view(), name='jj_status'),
|
||||
path('think/<slug:slug>/jj/commit', JJCommitView.as_view(), name='jj_commit'),
|
||||
path('new', CreateThinkView.as_view(), name='new_think'),
|
||||
path('new/<slug:slug>', RemixThinkView.as_view(), name='remix_think'),
|
||||
]
|
||||
|
|
|
@ -223,3 +223,22 @@ class LogView(ThinkMixin, generic.DetailView):
|
|||
think = self.get_object()
|
||||
|
||||
return HttpResponse(think.get_log(), content_type='text/plain; charset=utf-8')
|
||||
|
||||
class JJStatusView(ThinkMixin, generic.detail.DetailView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
status = self.get_object().jj_controller.status()
|
||||
return JsonResponse({'status': status})
|
||||
|
||||
class JJCommitView(ThinkMixin, generic.UpdateView):
|
||||
form_class = forms.GitCommitForm
|
||||
|
||||
def form_valid(self, form):
|
||||
message = form.cleaned_data['message']
|
||||
|
||||
think = form.instance
|
||||
|
||||
jj = think.jj_controller
|
||||
|
||||
res = jj.commit(message)
|
||||
|
||||
return JsonResponse({'ok': res.returncode == 0, 'think': think.pk, 'stdout': res.stdout, 'stderr': res.stderr})
|
||||
|
|
|
@ -131,3 +131,5 @@ THINKS_DIR.mkdir(parents=True, exist_ok=True)
|
|||
THINKS_STATIC_URL = 'https://{slug}.think.somethingorotherwhatever.com'
|
||||
|
||||
API_TOKEN = ''
|
||||
|
||||
GIT_REPO_URL_TEMPLATE = ''
|
||||
|
|
Loading…
Reference in a new issue