Compare commits
9 Commits
a43c1d3d1e
...
ddce88dce6
Author | SHA1 | Date | |
---|---|---|---|
ddce88dce6 | |||
6aa2bce2be | |||
2d3e108fd9 | |||
7822b30dcb | |||
e361c3f975 | |||
1d5cdf9607 | |||
a4bf3542e0 | |||
df82cfe66b | |||
53f9547cc5 |
@ -2,8 +2,9 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<base target="_top" />
|
<base target="_top" />
|
||||||
|
<link rel="stylesheet" href="tildefriends.css" />
|
||||||
</head>
|
</head>
|
||||||
<body style="color: #fff">
|
<body>
|
||||||
<tf-collections-app></tf-collections-app>
|
<tf-collections-app></tf-collections-app>
|
||||||
<script>
|
<script>
|
||||||
window.litDisableBundleWarning = true;
|
window.litDisableBundleWarning = true;
|
||||||
|
@ -5,6 +5,7 @@ class TfCollectionElement extends LitElement {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
whoami: {type: String},
|
whoami: {type: String},
|
||||||
|
category: {type: String},
|
||||||
collection: {type: Object},
|
collection: {type: Object},
|
||||||
selected_id: {type: String},
|
selected_id: {type: String},
|
||||||
is_creating: {type: Boolean},
|
is_creating: {type: Boolean},
|
||||||
@ -75,9 +76,10 @@ class TfCollectionElement extends LitElement {
|
|||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
return html`
|
return html`
|
||||||
<span style="display: inline-flex; flex-direction: row">
|
<link rel="stylesheet" href="tildefriends.css"/>
|
||||||
|
<span class="inline-flex-row">
|
||||||
<select @change=${this.on_selected} id="select" value=${this.selected_id}>
|
<select @change=${this.on_selected} id="select" value=${this.selected_id}>
|
||||||
<option value="" ?selected=${this.selected_id === ''} disabled hidden>(select)</option>
|
<option value="" ?selected=${this.selected_id === ''} disabled hidden>(select ${this.category})</option>
|
||||||
${Object.values(this.collection ?? {})
|
${Object.values(this.collection ?? {})
|
||||||
.sort((x, y) => x.name.localeCompare(y.name))
|
.sort((x, y) => x.name.localeCompare(y.name))
|
||||||
.map(
|
.map(
|
||||||
@ -91,22 +93,22 @@ class TfCollectionElement extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
<span ?hidden=${!this.is_renaming || !this.whoami}>
|
<span ?hidden=${!this.is_renaming || !this.whoami}>
|
||||||
<span style="display: inline-flex; flex-direction: row; margin-left: 8px; margin-right: 8px">
|
<span class="inline-flex-row" style="margin-left: 8px; margin-right: 8px">
|
||||||
<label for="rename_name">🏷Rename to:</label>
|
<label for="rename_name">🏷Rename to:</label>
|
||||||
<input type="text" id="rename_name"></input>
|
<input type="text" id="rename_name"></input>
|
||||||
<button @click=${this.on_rename}>Rename ${this.type}</button>
|
<button @click=${this.on_rename}>Rename ${this.type}</button>
|
||||||
<button @click=${() => (self.is_renaming = false)}>x</button>
|
<button @click=${() => (self.is_renaming = false)}>x</button>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<button @click=${() => (self.is_renaming = true)} ?disabled=${this.is_renaming || !this.selected_id} ?hidden=${!this.whoami}>🏷</button>
|
<button class="yellow" @click=${() => (self.is_renaming = true)} ?disabled=${this.is_renaming || !this.selected_id} ?hidden=${!this.whoami}>🏷</button>
|
||||||
<button @click=${self.on_tombstone} ?disabled=${!this.selected_id} ?hidden=${!this.whoami}>🪦</button>
|
<button class="red" @click=${self.on_tombstone} ?disabled=${!this.selected_id} ?hidden=${!this.whoami}>🪦</button>
|
||||||
<span ?hidden=${!this.is_creating || !this.whoami}>
|
<span ?hidden=${!this.is_creating || !this.whoami}>
|
||||||
<label for="create_name">New ${this.type} name:</label>
|
<label for="create_name">New ${this.type} name:</label>
|
||||||
<input type="text" id="create_name"></input>
|
<input type="text" id="create_name"></input>
|
||||||
<button @click=${this.on_create}>Create ${this.type}</button>
|
<button @click=${this.on_create}>Create ${this.type}</button>
|
||||||
<button @click=${() => (self.is_creating = false)}>x</button>
|
<button @click=${() => (self.is_creating = false)}>x</button>
|
||||||
</span>
|
</span>
|
||||||
<button @click=${() => (self.is_creating = true)} ?hidden=${this.is_creating || !this.whoami}>+</button>
|
<button class="green" @click=${() => (self.is_creating = true)} ?hidden=${this.is_creating || !this.whoami}>+</button>
|
||||||
</span>
|
</span>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ class TfIdentityPickerElement extends LitElement {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
|
<link rel="stylesheet" href="tildefriends.css" />
|
||||||
<select @change=${this.changed} style="max-width: 100%">
|
<select @change=${this.changed} style="max-width: 100%">
|
||||||
${(this.ids ?? []).map(
|
${(this.ids ?? []).map(
|
||||||
(id) =>
|
(id) =>
|
||||||
|
@ -255,12 +255,22 @@ class TfCollectionsAppElement extends LitElement {
|
|||||||
render() {
|
render() {
|
||||||
let self = this;
|
let self = this;
|
||||||
return html`
|
return html`
|
||||||
|
<link rel="stylesheet" href="tildefriends.css"/>
|
||||||
<style>
|
<style>
|
||||||
.toc:hover {
|
.toc-item {
|
||||||
|
white-space: nowrap;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.toc-item:hover {
|
||||||
background-color: #0cc;
|
background-color: #0cc;
|
||||||
}
|
}
|
||||||
.toc.selected {
|
.toc-item.selected {
|
||||||
background-color: #088;
|
background-color: #088;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.table-of-contents {
|
||||||
|
flex: 0 0;
|
||||||
|
margin-right: 16px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<div>
|
<div>
|
||||||
@ -272,6 +282,7 @@ class TfCollectionsAppElement extends LitElement {
|
|||||||
html`<tf-collection
|
html`<tf-collection
|
||||||
.collection=${this.wikis}
|
.collection=${this.wikis}
|
||||||
whoami=${this.whoami}
|
whoami=${this.whoami}
|
||||||
|
category="wiki"
|
||||||
selected_id=${this.wiki?.id}
|
selected_id=${this.wiki?.id}
|
||||||
@create=${this.on_wiki_create}
|
@create=${this.on_wiki_create}
|
||||||
@rename=${this.on_wiki_rename}
|
@rename=${this.on_wiki_rename}
|
||||||
@ -284,6 +295,7 @@ class TfCollectionsAppElement extends LitElement {
|
|||||||
html`<tf-collection
|
html`<tf-collection
|
||||||
.collection=${this.wiki_docs}
|
.collection=${this.wiki_docs}
|
||||||
whoami=${this.whoami}
|
whoami=${this.whoami}
|
||||||
|
category="document"
|
||||||
selected_id=${this.wiki_doc &&
|
selected_id=${this.wiki_doc &&
|
||||||
this.wiki_doc?.parent == this.wiki?.id
|
this.wiki_doc?.parent == this.wiki?.id
|
||||||
? this.wiki_doc?.id
|
? this.wiki_doc?.id
|
||||||
@ -298,9 +310,9 @@ class TfCollectionsAppElement extends LitElement {
|
|||||||
<div ?hidden=${!this.wiki?.editors || !this.expand_editors}>
|
<div ?hidden=${!this.wiki?.editors || !this.expand_editors}>
|
||||||
<div>
|
<div>
|
||||||
<ul>
|
<ul>
|
||||||
${this.wiki?.editors.map((id) => html`<li><button ?hidden=${id == this.whoami} @click=${() => self.on_remove_editor(id)}>x</button> ${id}</li>`)}
|
${this.wiki?.editors.map((id) => html`<li><button class="red" ?hidden=${id == this.whoami} @click=${() => self.on_remove_editor(id)}>x</button> ${id}</li>`)}
|
||||||
<li>
|
<li>
|
||||||
<button @click=${() => (self.adding_editor = true)} ?hidden=${this.wiki?.editors?.indexOf(this.whoami) == -1 || this.adding_editor}>+</button>
|
<button class="green" @click=${() => (self.adding_editor = true)} ?hidden=${this.wiki?.editors?.indexOf(this.whoami) == -1 || this.adding_editor}>+</button>
|
||||||
<div ?hidden=${!this.adding_editor}>
|
<div ?hidden=${!this.adding_editor}>
|
||||||
<label for="add_editor">Add Editor:</label>
|
<label for="add_editor">Add Editor:</label>
|
||||||
<input type="text" id="add_editor"></input>
|
<input type="text" id="add_editor"></input>
|
||||||
@ -312,18 +324,19 @@ class TfCollectionsAppElement extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: flex; flex-direction: row">
|
<div class="flex-row">
|
||||||
<div style="flex: 0 0">
|
<div class="box table-of-contents">
|
||||||
${Object.values(this.wikis || {})
|
${Object.values(this.wikis || {})
|
||||||
.sort((x, y) => x.name.localeCompare(y.name))
|
.sort((x, y) => x.name.localeCompare(y.name))
|
||||||
.map(
|
.map(
|
||||||
(wiki) => html`
|
(wiki) => html`
|
||||||
<div
|
<div
|
||||||
class="toc ${self.wiki?.id === wiki.id ? 'selected' : ''}"
|
class="toc-item ${self.wiki?.id === wiki.id
|
||||||
style="white-space: nowrap; cursor: pointer"
|
? 'selected'
|
||||||
|
: ''}"
|
||||||
@click=${() => self.on_wiki_changed({detail: {value: wiki}})}
|
@click=${() => self.on_wiki_changed({detail: {value: wiki}})}
|
||||||
>
|
>
|
||||||
${wiki.name}
|
${self.wiki?.id === wiki.id ? '' : '>'} ${wiki.name}
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
${Object.values(self.wiki_docs || {})
|
${Object.values(self.wiki_docs || {})
|
||||||
@ -332,10 +345,10 @@ class TfCollectionsAppElement extends LitElement {
|
|||||||
.map(
|
.map(
|
||||||
(doc) => html`
|
(doc) => html`
|
||||||
<li
|
<li
|
||||||
class="toc ${self.wiki_doc?.id === doc.id
|
class="toc-item ${self.wiki_doc?.id === doc.id
|
||||||
? 'selected'
|
? 'selected'
|
||||||
: ''}"
|
: ''}"
|
||||||
style="white-space: nowrap; cursor: pointer; list-style: none; text-indent: -1rem"
|
style="list-style: none; text-indent: -1rem"
|
||||||
@click=${() =>
|
@click=${() =>
|
||||||
self.on_wiki_doc_changed({detail: {value: doc}})}
|
self.on_wiki_doc_changed({detail: {value: doc}})}
|
||||||
>
|
>
|
||||||
|
@ -84,7 +84,11 @@ class TfWikiDocElement extends LitElement {
|
|||||||
|
|
||||||
async load_blob() {
|
async load_blob() {
|
||||||
let blob = await tfrpc.rpc.get_blob(this.value?.blob);
|
let blob = await tfrpc.rpc.get_blob(this.value?.blob);
|
||||||
if (blob.endsWith('.box')) {
|
if (!blob) {
|
||||||
|
console.warn(
|
||||||
|
"no blob found, we're going to assume the document is empty (load_blob())"
|
||||||
|
);
|
||||||
|
} else if (blob.endsWith('.box')) {
|
||||||
let d = await tfrpc.rpc.try_decrypt(this.whoami, blob);
|
let d = await tfrpc.rpc.try_decrypt(this.whoami, blob);
|
||||||
if (d) {
|
if (d) {
|
||||||
blob = d;
|
blob = d;
|
||||||
@ -253,85 +257,43 @@ class TfWikiDocElement extends LitElement {
|
|||||||
let self = this;
|
let self = this;
|
||||||
let thumbnail_ref = this.thumbnail(this.blob);
|
let thumbnail_ref = this.thumbnail(this.blob);
|
||||||
return html`
|
return html`
|
||||||
|
<link rel="stylesheet" href="tildefriends.css"/>
|
||||||
<style>
|
<style>
|
||||||
a:link {
|
a:link { color: #268bd2 }
|
||||||
color: #268bd2;
|
a:visited { color: #6c71c4 }
|
||||||
}
|
a:hover { color: #859900 }
|
||||||
a:visited {
|
a:active { color: #2aa198 }
|
||||||
color: #6c71c4;
|
|
||||||
}
|
#editor-text-area {
|
||||||
a:hover {
|
background-color: #00000040;
|
||||||
color: #859900;
|
color: white;
|
||||||
}
|
style="flex: 1 1;
|
||||||
a:active {
|
min-height: 10em;
|
||||||
color: #2aa198;
|
font-size: larger;
|
||||||
}
|
${this.value?.private ? 'border: 4px solid #800' : ''}
|
||||||
</style>
|
</style>
|
||||||
<div style="display: inline-flex; flex-direction: row">
|
<div class="inline-flex-row">
|
||||||
<button
|
<button ?disabled=${!this.whoami || this.is_editing} @click=${() => (self.is_editing = true)}>Edit</button>
|
||||||
?disabled=${!this.whoami || this.is_editing}
|
<button ?disabled=${this.blob == this.blob_original} @click=${this.on_save_draft}>Save Draft</button>
|
||||||
@click=${() => (self.is_editing = true)}
|
<button ?disabled=${this.blob == this.blob_original && !this.value?.draft} @click=${this.on_publish}>Publish</button>
|
||||||
>
|
<button ?disabled=${!this.is_editing} @click=${this.on_discard}>Discard</button>
|
||||||
Edit
|
<button ?disabled=${!this.is_editing} @click=${() => (self.value = Object.assign({}, self.value, {private: !self.value.private}))}>${this.value?.private ? 'Make Public' : 'Make Private'}</button>
|
||||||
</button>
|
<button ?disabled=${!this.is_editing} @click=${this.on_blog_publish}>Publish Blog</button>
|
||||||
<button
|
|
||||||
?disabled=${this.blob == this.blob_original}
|
|
||||||
@click=${this.on_save_draft}
|
|
||||||
>
|
|
||||||
Save Draft
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
?disabled=${this.blob == this.blob_original && !this.value?.draft}
|
|
||||||
@click=${this.on_publish}
|
|
||||||
>
|
|
||||||
Publish
|
|
||||||
</button>
|
|
||||||
<button ?disabled=${!this.is_editing} @click=${this.on_discard}>
|
|
||||||
Discard
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
?disabled=${!this.is_editing}
|
|
||||||
@click=${() =>
|
|
||||||
(self.value = Object.assign({}, self.value, {
|
|
||||||
private: !self.value.private,
|
|
||||||
}))}
|
|
||||||
>
|
|
||||||
${this.value?.private ? 'Make Public' : 'Make Private'}
|
|
||||||
</button>
|
|
||||||
<button ?disabled=${!this.is_editing} @click=${this.on_blog_publish}>
|
|
||||||
Publish Blog
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
<div ?hidden=${!this.value?.private} style="color: #800">
|
<div ?hidden=${!this.value?.private} style="color: #800">🔒 document is private</div>
|
||||||
🔒 document is private
|
<div class="flex-column" ${this.value?.private ? 'border-top: 4px solid #800' : ''}">
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="display: flex; flex-direction: row; ${this.value?.private
|
|
||||||
? 'border-top: 4px solid #800'
|
|
||||||
: ''}"
|
|
||||||
>
|
|
||||||
<textarea
|
<textarea
|
||||||
|
rows="25"
|
||||||
?hidden=${!this.is_editing}
|
?hidden=${!this.is_editing}
|
||||||
style="flex: 1 1; min-height: 10em; ${this.value?.private
|
id="editor-text-area"
|
||||||
? 'border: 4px solid #800'
|
|
||||||
: ''}"
|
|
||||||
@input=${this.on_edit}
|
@input=${this.on_edit}
|
||||||
@paste=${this.paste}
|
@paste=${this.paste}
|
||||||
.value=${this.blob ?? ''}
|
.value=${this.blob ?? ''}></textarea>
|
||||||
></textarea>
|
<div style="flex: 1 1; margin-top: 16px">
|
||||||
<div style="flex: 1 1">
|
<div ?hidden=${!this.is_editing} class="box">
|
||||||
<div
|
Summary
|
||||||
?hidden=${!this.is_editing}
|
<img ?hidden=${!thumbnail_ref} style="max-width: 128px; max-height: 128px; float: right" src="/${thumbnail_ref}/view">
|
||||||
style="border: 1px solid #fff; border-radius: 1em; padding: 0.5em"
|
<h1 ?hidden=${!this.title(this.blob)}>${unsafeHTML(this.markdown(this.title(this.blob)))}</h1>
|
||||||
>
|
|
||||||
<img
|
|
||||||
?hidden=${!thumbnail_ref}
|
|
||||||
style="max-width: 128px; max-height: 128px; float: right"
|
|
||||||
src="/${thumbnail_ref}/view"
|
|
||||||
/>
|
|
||||||
<h1 ?hidden=${!this.title(this.blob)}>
|
|
||||||
${unsafeHTML(this.markdown(this.title(this.blob)))}
|
|
||||||
</h1>
|
|
||||||
${unsafeHTML(this.markdown(this.summary(this.blob)))}
|
${unsafeHTML(this.markdown(this.summary(this.blob)))}
|
||||||
</div>
|
</div>
|
||||||
${unsafeHTML(this.markdown(this.blob))}
|
${unsafeHTML(this.markdown(this.blob))}
|
||||||
|
115
apps/wiki/tildefriends.css
Normal file
115
apps/wiki/tildefriends.css
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Tilde Friends core stylesheet
|
||||||
|
* This is a prototype; things may change based on feedback.
|
||||||
|
*
|
||||||
|
* This Software is an external library that is part of
|
||||||
|
* Tilde Friends and is shared under the MIT license.
|
||||||
|
*
|
||||||
|
* Inject this file in your app at tildefriends.css
|
||||||
|
* and use this tag to import it:
|
||||||
|
* <link rel="stylesheet" href="tildefriends.css"/>
|
||||||
|
*
|
||||||
|
* Revision 0 / 2024 M02 19
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
button,
|
||||||
|
.button,
|
||||||
|
input[type='button'],
|
||||||
|
input[type='submit'],
|
||||||
|
select {
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: center;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 4px;
|
||||||
|
|
||||||
|
&.red {
|
||||||
|
background-color: #bd1e24;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.green {
|
||||||
|
background-color: #18922d;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.blue {
|
||||||
|
background-color: #0067a7;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.yellow {
|
||||||
|
background-color: #ee9600;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
filter: brightness(0.75);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link {
|
||||||
|
color: #268bd2;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited {
|
||||||
|
color: #6c71c4;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #859900;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:active {
|
||||||
|
color: #2aa198;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
border: 1px solid #ffffff40;
|
||||||
|
text-align: left;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #ffffff20;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-column {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flex-row {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inline-flex-row {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
background-color: #00000020;
|
||||||
|
border: 1px solid grey;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
margin: 4px;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user