bookish-press 0.4.23 → 0.4.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/components/Auth.svelte +11 -13
  2. package/components/BookPreview.svelte +12 -14
  3. package/components/app/Feedback.svelte +12 -10
  4. package/components/app/Head.svelte +4 -1
  5. package/components/app/Head.svelte.d.ts +6 -15
  6. package/components/app/Paragraph.svelte +1 -0
  7. package/components/app/{Writing.svelte → Section.svelte} +0 -1
  8. package/components/app/Section.svelte.d.ts +27 -0
  9. package/components/app/TextInput.svelte +1 -0
  10. package/components/chapter/Block.svelte +3 -1
  11. package/components/chapter/CaptionedCode.svelte +2 -2
  12. package/components/chapter/ChapterBody.svelte +2 -1
  13. package/components/chapter/ChapterBody.svelte.d.ts +1 -0
  14. package/components/chapter/Comment.svelte +2 -2
  15. package/components/chapter/Embed.svelte +3 -3
  16. package/components/chapter/Embed.svelte.d.ts +1 -0
  17. package/components/chapter/Figure.svelte +2 -2
  18. package/components/chapter/Format.svelte +5 -3
  19. package/components/chapter/Format.svelte.d.ts +1 -0
  20. package/components/chapter/InlineCode.svelte +2 -2
  21. package/components/chapter/Label.svelte +2 -2
  22. package/components/chapter/Link.svelte +2 -2
  23. package/components/chapter/Marginal.svelte +2 -2
  24. package/components/chapter/Paragraph.svelte +5 -0
  25. package/components/chapter/Table.svelte +2 -2
  26. package/components/chapter/Text.svelte +2 -2
  27. package/components/editor/BookishEditor.svelte +51 -28
  28. package/components/editor/BookishEditor.svelte.d.ts +4 -1
  29. package/components/editor/CaretView.svelte +3 -1
  30. package/components/editor/CaretView.svelte.d.ts +1 -0
  31. package/components/editor/ConfirmButton.svelte +1 -1
  32. package/components/editor/Note.svelte +1 -0
  33. package/components/editor/Permissions.svelte +95 -0
  34. package/components/editor/Permissions.svelte.d.ts +20 -0
  35. package/components/editor/Toolbar.svelte +0 -1
  36. package/components/editor/ToolbarGroup.svelte +0 -1
  37. package/components/page/Acknowledgements.svelte +13 -11
  38. package/components/page/Authors.svelte +5 -8
  39. package/components/page/Authors.svelte.d.ts +1 -0
  40. package/components/page/BookLoader.svelte +59 -0
  41. package/components/page/BookLoader.svelte.d.ts +16 -0
  42. package/components/page/BulkReferenceEditor.svelte +8 -8
  43. package/components/page/Chapter.svelte +43 -10
  44. package/components/page/Contexts.d.ts +14 -5
  45. package/components/page/Contexts.js +88 -6
  46. package/components/page/DefinitionView.svelte +18 -8
  47. package/components/page/Description.svelte +12 -9
  48. package/components/page/Edition.svelte +8 -8
  49. package/components/page/Edition.svelte.d.ts +0 -1
  50. package/components/page/EditionLoader.svelte +99 -0
  51. package/components/page/EditionLoader.svelte.d.ts +18 -0
  52. package/components/page/Editions.svelte +92 -143
  53. package/components/page/Glossary.svelte +13 -6
  54. package/components/page/Header.svelte +22 -20
  55. package/components/page/Header.svelte.d.ts +2 -0
  56. package/components/page/Index.svelte +6 -4
  57. package/components/page/Instructions.svelte +1 -2
  58. package/components/page/Instructions.svelte.d.ts +3 -1
  59. package/components/page/License.svelte +20 -12
  60. package/components/page/Media.svelte +14 -12
  61. package/components/page/Publish.svelte +120 -0
  62. package/components/page/Publish.svelte.d.ts +14 -0
  63. package/components/page/Reference.svelte +16 -17
  64. package/components/page/References.svelte +8 -3
  65. package/components/page/Search.svelte +3 -1
  66. package/components/page/Status.svelte +18 -19
  67. package/components/page/SubdomainEditor.svelte +3 -2
  68. package/components/page/TableOfContents.svelte +70 -30
  69. package/components/page/TableOfContentsRow.svelte +4 -4
  70. package/components/page/ThemeEditor.svelte +8 -5
  71. package/components/page/Unknown.svelte +5 -2
  72. package/models/CRUD.d.ts +19 -11
  73. package/models/CRUD.js +177 -122
  74. package/models/Firebase.js +12 -8
  75. package/models/book/Book.d.ts +19 -14
  76. package/models/book/Book.js +47 -54
  77. package/models/book/Chapter.d.ts +5 -1
  78. package/models/book/Chapter.js +24 -15
  79. package/models/book/Edition.d.ts +31 -7
  80. package/models/book/Edition.js +101 -32
  81. package/models/chapter/Parser.d.ts +2 -2
  82. package/models/chapter/Parser.js +26 -24
  83. package/package.json +18 -15
  84. package/components/app/Writing.svelte.d.ts +0 -27
  85. package/models/loadEdition.d.ts +0 -5
  86. package/models/loadEdition.js +0 -20
@@ -16,17 +16,19 @@ import EmbedNode from '../../models/chapter/EmbedNode';
16
16
  import ErrorNode from '../../models/chapter/ErrorNode';
17
17
  import { caretRangeToIndexRange, indexRangeToCaretRange, } from '../../models/chapter/Caret';
18
18
  import commands from './Commands';
19
- import { getEdition } from '../page/Contexts';
20
19
  import { afterUpdate, onMount, setContext } from 'svelte';
21
20
  import { getCaret } from '../page/Contexts';
22
21
  import CaretView from './CaretView.svelte';
23
22
  const IDLE_TIME = 500;
24
- export let ast;
23
+ export let text;
24
+ export let parser;
25
25
  export let save;
26
26
  export let chapter;
27
27
  export let autofocus = false;
28
28
  export let component;
29
29
  export let placeholder;
30
+ export let leasee;
31
+ export let lease;
30
32
  let element = null;
31
33
  let caretRange = undefined;
32
34
  let caretCoordinate = undefined;
@@ -36,10 +38,19 @@ let editorFocused = false;
36
38
  let undoStack = [];
37
39
  let undoPosition = -1;
38
40
  let clipboard = undefined;
39
- let editedNode = ast;
40
41
  let ignoredInput = false;
41
- let edition = getEdition();
42
42
  let activeEditor = getCaret();
43
+ let editedNode;
44
+ $: {
45
+ // Note: when the text changes externally, the caret will be lost. We either have
46
+ // to implement fancy operational transform revision control for collaborative editing
47
+ // or a basic chapter locking strategy to avoid data loss. I went with locking for maintenance simplicity,
48
+ // but it would be nice to implement operational transform in the future.
49
+ if (editedNode === undefined || editedNode.toBookdown() !== text) {
50
+ editedNode = parser(text);
51
+ }
52
+ }
53
+ $: locked = typeof leasee === 'string';
43
54
  onMount(() => {
44
55
  const caret = editedNode.getFirstCaret();
45
56
  if (caret)
@@ -357,17 +368,11 @@ function undo() {
357
368
  // Grab the next state
358
369
  let undoState = undoStack[undoPosition + 1];
359
370
  // Restore the content of the chapter.
360
- let node = ast instanceof ChapterNode
361
- ? Parser.parseChapter($edition, undoState.bookdown)
362
- : ast instanceof FormatNode
363
- ? Parser.parseFormat($edition, undoState.bookdown)
364
- : ast instanceof EmbedNode
365
- ? Parser.parseEmbed($edition, undoState.bookdown)
366
- : undefined;
371
+ let node = parser(undoState.bookdown);
367
372
  if (node === undefined || node instanceof ErrorNode)
368
373
  return;
369
374
  // Restore the view
370
- editedNode = node;
375
+ updateRoot(node);
371
376
  // Save the undo.
372
377
  save(node);
373
378
  // Move the undo state down a position.
@@ -383,16 +388,10 @@ function redo() {
383
388
  // Grab the next state
384
389
  let undoState = undoStack[undoPosition - 1];
385
390
  // Restore the content of the chapter.
386
- let node = ast instanceof ChapterNode
387
- ? Parser.parseChapter($edition, undoState.bookdown)
388
- : ast instanceof FormatNode
389
- ? Parser.parseFormat($edition, undoState.bookdown)
390
- : ast instanceof EmbedNode
391
- ? Parser.parseEmbed($edition, undoState.bookdown)
392
- : undefined;
391
+ let node = parser(undoState.bookdown);
393
392
  if (node === undefined)
394
393
  return;
395
- editedNode = node;
394
+ updateRoot(node);
396
395
  // Move the undo state down a position.
397
396
  if (undoPosition > 0)
398
397
  undoPosition = undoPosition - 1;
@@ -588,6 +587,9 @@ function executeCommand(command, key) {
588
587
  return false;
589
588
  }
590
589
  async function saveEdit(newRoot, newRange, command) {
590
+ // Don't save edits if locked.
591
+ if (locked)
592
+ return;
591
593
  // Call the save callback.
592
594
  save(newRoot);
593
595
  // Initialize a new history if we don't have one.
@@ -605,7 +607,7 @@ async function saveEdit(newRoot, newRange, command) {
605
607
  ];
606
608
  }
607
609
  // Change the chapter's AST.
608
- editedNode = newRoot;
610
+ updateRoot(newRoot);
609
611
  // Set the new undo stack, pre-pending the new command to the front.
610
612
  const newTextRange = caretRangeToIndexRange(newRoot, newRange);
611
613
  if (newTextRange !== undefined)
@@ -626,6 +628,10 @@ async function saveEdit(newRoot, newRange, command) {
626
628
  // Update the caret
627
629
  claimActiveEditor();
628
630
  }
631
+ function updateRoot(newRoot) {
632
+ editedNode = newRoot;
633
+ text = editedNode.toBookdown();
634
+ }
629
635
  function editNode(previous, edited) {
630
636
  if (caretRange) {
631
637
  const newRoot = editedNode.withNodeReplaced(previous, edited);
@@ -663,8 +669,13 @@ function isFocused() {
663
669
  (element !== null && element?.contains(document.activeElement)) ||
664
670
  toolbarIsFocused());
665
671
  }
666
- function updateFocus() {
672
+ function handleFocus() {
673
+ editorFocused = isFocused();
674
+ lease(true);
675
+ }
676
+ function handleBlur() {
667
677
  editorFocused = isFocused();
678
+ lease(false);
668
679
  }
669
680
  function handleCopy(node) {
670
681
  // Set the editor's clipboard.
@@ -793,16 +804,23 @@ setContext('claimeditor', claimActiveEditor);
793
804
 
794
805
  <div
795
806
  class={`bookish-editor ${inAtom ? 'bookish-editor-atom-focused' : ''}`}
807
+ class:locked
808
+ aria-disabled={locked}
796
809
  bind:this={element}
797
810
  on:keydown={handleKey}
798
811
  on:keypress={handlePress}
799
812
  on:mousedown|stopPropagation={handleMouseDown}
800
- on:focus={updateFocus}
801
- on:blur={updateFocus}
813
+ on:focus={handleFocus}
814
+ on:blur={handleBlur}
802
815
  tabIndex="0"
803
816
  >
804
- <!-- The chapter content -->
805
- <svelte:component this={component} node={editedNode} {placeholder} />
817
+ <!-- A view of the root -->
818
+ <svelte:component
819
+ this={component}
820
+ node={editedNode}
821
+ {placeholder}
822
+ editable={true}
823
+ />
806
824
  <!-- The caret. We render our own since this view isn't contentEditable and we can't show a caret.
807
825
  Customize the rendering based on the formatting applied to the text node. -->
808
826
  {#if caretCoordinate && caretRange && !isAtom && isSelection === false && editorFocused}
@@ -815,14 +833,19 @@ setContext('claimeditor', claimActiveEditor);
815
833
  linked={isLink}
816
834
  bold={isBold}
817
835
  italic={isItalic}
818
- disabled={!editorFocused}
836
+ disabled={!editorFocused || locked}
837
+ {locked}
819
838
  />
820
839
  {/if}
821
840
  </div>
822
841
 
823
842
  <style>
824
843
  .bookish-editor {
825
- min-height: 2em;
844
+ min-height: 1em;
845
+ }
846
+
847
+ .locked :global(.bookish-text) {
848
+ color: var(--app-muted-color);
826
849
  }
827
850
 
828
851
  .bookish-editor:focus {
@@ -2,12 +2,15 @@ import { SvelteComponentTyped } from "svelte";
2
2
  import type RootNode from '../../models/chapter/RootNode';
3
3
  declare const __propDef: {
4
4
  props: {
5
- ast: RootNode;
5
+ text: string;
6
+ parser: (text: string) => RootNode;
6
7
  save: (node: RootNode) => Promise<void> | void;
7
8
  chapter: boolean;
8
9
  autofocus?: boolean | undefined;
9
10
  component: ConstructorOfATypedSvelteComponent;
10
11
  placeholder: string;
12
+ leasee: string | boolean;
13
+ lease: (lock: boolean) => Promise<boolean> | boolean;
11
14
  };
12
15
  events: {
13
16
  [evt: string]: CustomEvent<any>;
@@ -8,6 +8,7 @@ export let bold;
8
8
  export let disabled;
9
9
  export let ignored;
10
10
  export let blink;
11
+ export let locked;
11
12
  let element;
12
13
  let headerHeight = 0;
13
14
  // When the caret updates, make sure it's in view.
@@ -32,7 +33,8 @@ afterUpdate(() => {
32
33
  style:height="{height}px"
33
34
  style:scroll-margin-top="{headerHeight}px"
34
35
  bind:this={element}
35
- />
36
+ >{#if locked}🔒{/if}</div
37
+ >
36
38
 
37
39
  <style>
38
40
  .caret {
@@ -10,6 +10,7 @@ declare const __propDef: {
10
10
  disabled: boolean;
11
11
  ignored: boolean;
12
12
  blink: boolean;
13
+ locked: boolean;
13
14
  };
14
15
  events: {
15
16
  [evt: string]: CustomEvent<any>;
@@ -43,7 +43,7 @@ function execute() {
43
43
  </Button>
44
44
 
45
45
  <style>
46
- :global(button.bookish-editor-confirm) {
46
+ :global(bookish-editor-confirm) {
47
47
  animation: failure 100ms infinite;
48
48
  }
49
49
  </style>
@@ -4,6 +4,7 @@
4
4
  span {
5
5
  font-size: small;
6
6
  font-style: italic;
7
+ font-weight: normal;
7
8
  color: var(--app-muted-color);
8
9
  }
9
10
  </style>
@@ -0,0 +1,95 @@
1
+ <script>import { createUser, getUserEmails } from '../../models/CRUD';
2
+ import Button from '../app/Button.svelte';
3
+ import TextInput from '../app/TextInput.svelte';
4
+ import Note from './Note.svelte';
5
+ export let uids;
6
+ export let inheriteduids;
7
+ export let writable;
8
+ export let atleastone;
9
+ export let change;
10
+ let emails = null;
11
+ $: alluids = Array.from(new Set([...uids, ...inheriteduids]));
12
+ async function getEmails(userIDs) {
13
+ emails = await getUserEmails(userIDs);
14
+ }
15
+ $: {
16
+ if (emails === null ||
17
+ (emails.size > 0 &&
18
+ Array.from(emails.keys()).join() !== alluids.join()))
19
+ getEmails(alluids);
20
+ }
21
+ let newEditor = '';
22
+ let newEditorError = null;
23
+ let loading = undefined;
24
+ async function addEditor() {
25
+ const email = newEditor;
26
+ newEditor = '';
27
+ loading = email;
28
+ try {
29
+ const uid = await createUser(email);
30
+ if (uid === null)
31
+ newEditorError = "couldn't add editor";
32
+ else
33
+ change([...uids, uid]);
34
+ }
35
+ finally {
36
+ loading = undefined;
37
+ }
38
+ }
39
+ </script>
40
+
41
+ {#if emails === null}
42
+ Loading editors...
43
+ {:else}
44
+ <section class="emails">
45
+ {#if writable}
46
+ <form on:submit|preventDefault={addEditor}>
47
+ <TextInput
48
+ type="email"
49
+ placeholder="email"
50
+ bind:text={newEditor}
51
+ disabled={false}
52
+ />
53
+ <Button
54
+ tooltip="give {newEditor} editing rights"
55
+ disabled={newEditor.length === 0 ||
56
+ Array.from(emails.values()).includes(newEditor)}
57
+ command={addEditor}>+ editor</Button
58
+ >
59
+ {#if newEditorError !== null}{newEditorError}{/if}
60
+ </form>
61
+ {/if}
62
+
63
+ {#each uids as uid, index (uid)}
64
+ {@const email = emails.get(uid)}
65
+ <p>
66
+ {email}
67
+ {#if writable}
68
+ <Button
69
+ tooltip="remove {email} editing rights"
70
+ command={() =>
71
+ change([
72
+ ...uids.slice(0, index),
73
+ ...uids.slice(index + 1),
74
+ ])}
75
+ disabled={atleastone && uids.length <= 1}>x</Button
76
+ >
77
+ {/if}
78
+ </p>
79
+ {/each}
80
+ {#each inheriteduids as uid}
81
+ <p><em>{emails.get(uid)}</em> <Note>book editor</Note></p>
82
+ {/each}
83
+ {#if loading}
84
+ <p>
85
+ adding {loading}
86
+ </p>
87
+ {/if}
88
+ </section>
89
+ {/if}
90
+
91
+ <style>
92
+ p {
93
+ font-family: var(--app-font);
94
+ }
95
+ </style>
@@ -0,0 +1,20 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: {
4
+ uids: string[];
5
+ inheriteduids: string[];
6
+ writable: boolean;
7
+ atleastone: boolean;
8
+ change: (uids: string[]) => void;
9
+ };
10
+ events: {
11
+ [evt: string]: CustomEvent<any>;
12
+ };
13
+ slots: {};
14
+ };
15
+ export type PermissionsProps = typeof __propDef.props;
16
+ export type PermissionsEvents = typeof __propDef.events;
17
+ export type PermissionsSlots = typeof __propDef.slots;
18
+ export default class Permissions extends SvelteComponentTyped<PermissionsProps, PermissionsEvents, PermissionsSlots> {
19
+ }
20
+ export {};
@@ -239,7 +239,6 @@ function handleKeyPress(event) {
239
239
  flex-wrap: wrap;
240
240
  align-items: baseline;
241
241
  background: var(--bookish-background-color);
242
- padding: var(--app-chrome-padding);
243
242
  font-family: var(--app-font);
244
243
  font-size: var(--app-chrome-font-size);
245
244
  }
@@ -10,7 +10,6 @@ export let icon;
10
10
  <style>
11
11
  .toolbar-group {
12
12
  display: inline-block;
13
- padding-bottom: var(--app-chrome-padding);
14
13
  margin-right: calc(2 * var(--app-chrome-padding));
15
14
  }
16
15
 
@@ -2,37 +2,39 @@
2
2
  import ChapterBody from '../chapter/ChapterBody.svelte';
3
3
  import BookishEditor from '../editor/BookishEditor.svelte';
4
4
  import Instructions from './Instructions.svelte';
5
- import { getEdition, isEditable } from './Contexts';
5
+ import { getUser, getEdition, getLeasee, isEditionEditable, lease, } from './Contexts';
6
6
  import PageHeader from './PageHeader.svelte';
7
+ let auth = getUser();
7
8
  let edition = getEdition();
8
- let editable = isEditable();
9
+ let editable = isEditionEditable();
9
10
  $: acknowledgements = $edition?.getAcknowledgements();
10
- $: acksNode = acknowledgements
11
- ? Parser.parseChapter($edition, acknowledgements)
12
- : undefined;
13
11
  </script>
14
12
 
15
- <Instructions>
16
- This section is not shown if empty. But surely you have someone to thank!
13
+ <Instructions {editable}>
14
+ Do you have people, communities, or institutions to thank? Do it here. This
15
+ section is not shown if empty.
17
16
  </Instructions>
18
17
 
19
18
  <!-- If editable, show acknowledgements even if they're empty, otherwise hide -->
20
- {#if $edition && acknowledgements && acksNode}
19
+ {#if $edition && acknowledgements}
21
20
  {#if editable}
22
21
  <BookishEditor
23
- ast={acksNode}
22
+ text={acknowledgements}
23
+ parser={(text) => Parser.parseChapter($edition, text)}
24
24
  chapter={false}
25
25
  component={ChapterBody}
26
26
  placeholder="Who would you like to thank?"
27
27
  save={(node) =>
28
28
  $edition
29
29
  ? edition.set(
30
- $edition.setAcknowledgements(node.toBookdown())
30
+ $edition.withAcknowledgements(node.toBookdown())
31
31
  )
32
32
  : undefined}
33
+ leasee={getLeasee(auth, edition, `acks`)}
34
+ lease={(lock) => lease(auth, edition, `acks`, lock)}
33
35
  />
34
36
  {:else if acknowledgements.length > 0}
35
37
  <PageHeader id="acknowledgements">Acknowledgements</PageHeader>
36
- <ChapterBody node={acksNode} />
38
+ <ChapterBody node={Parser.parseChapter($edition, acknowledgements)} />
37
39
  {/if}
38
40
  {/if}
@@ -2,15 +2,15 @@
2
2
  import TextEditor from '../editor/TextEditor.svelte';
3
3
  import Format from '../chapter/Format.svelte';
4
4
  import { afterUpdate } from 'svelte';
5
- import { getEdition, isEditable } from './Contexts';
5
+ import { getEdition, isBookEditable, isEditionEditable } from './Contexts';
6
6
  import Note from '../editor/Note.svelte';
7
7
  import Button from '../app/Button.svelte';
8
+ export let editable;
8
9
  export let authors;
9
10
  export let inheritedAuthors = undefined;
10
11
  export let add;
11
12
  export let edit;
12
13
  export let remove;
13
- let editable = isEditable();
14
14
  let edition = getEdition();
15
15
  let authorList = null;
16
16
  let newAuthor = false;
@@ -47,7 +47,7 @@ afterUpdate(() => {
47
47
  <TextEditor
48
48
  text={author}
49
49
  label={'Author name editor'}
50
- placeholder="Author"
50
+ placeholder="author"
51
51
  valid={() => undefined}
52
52
  save={(text) =>
53
53
  /* If text is already empty, remove */ author.length >
@@ -66,9 +66,7 @@ afterUpdate(() => {
66
66
  {/each}
67
67
  {/if}
68
68
  {#if editable}
69
- &nbsp;<Button tooltip="Add an author" command={addAuthor}
70
- >+ author</Button
71
- >
69
+ &nbsp;<Button tooltip="add author" command={addAuthor}>+ author</Button>
72
70
  {/if}
73
71
  </p>
74
72
 
@@ -76,7 +74,6 @@ afterUpdate(() => {
76
74
  .bookish-authors {
77
75
  font-family: var(--bookish-header-font-family);
78
76
  font-style: italic;
79
- margin-top: 0;
80
- margin-bottom: var(--bookish-paragraph-spacing);
77
+ margin: 0;
81
78
  }
82
79
  </style>
@@ -1,6 +1,7 @@
1
1
  import { SvelteComponentTyped } from "svelte";
2
2
  declare const __propDef: {
3
3
  props: {
4
+ editable: boolean;
4
5
  authors: string[];
5
6
  inheritedAuthors?: string[] | undefined;
6
7
  add: () => Promise<void> | void;
@@ -0,0 +1,59 @@
1
+ <script>import Loading from './Loading.svelte';
2
+ import Feedback from '../app/Feedback.svelte';
3
+ import { onDestroy } from 'svelte';
4
+ import { page } from '$app/stores';
5
+ import { getBook, getUser } from './Contexts';
6
+ import { listenToBookWithID as listenToBooksWithID, listenToBooksByName as listenToBooksWithName, } from '../../models/CRUD';
7
+ let auth = getUser();
8
+ let book = getBook();
9
+ // Keep track of the query updates
10
+ let booksByName = undefined;
11
+ let bookByID = undefined;
12
+ // Mege them together here
13
+ $: currentBook =
14
+ // Neither loaded? Current book isn't loaded.
15
+ booksByName === undefined && bookByID === undefined
16
+ ? undefined
17
+ : booksByName !== undefined && booksByName.length > 0
18
+ ? booksByName[0]
19
+ : bookByID ?? null;
20
+ // Whenever the merge changes, update the book store (updating the whole UI)
21
+ $: book.set(currentBook ?? undefined);
22
+ // Keep track of listeners to unsubscribe to on page changes.
23
+ let nameUnsub = undefined;
24
+ let idUnsub = undefined;
25
+ function unsub() {
26
+ if (nameUnsub)
27
+ nameUnsub();
28
+ if (idUnsub)
29
+ idUnsub();
30
+ }
31
+ // Keep track of any errors.
32
+ let error;
33
+ function listen() {
34
+ unsub();
35
+ try {
36
+ nameUnsub = listenToBooksWithName($page.params.bookid, (books) => (booksByName = books));
37
+ idUnsub = listenToBooksWithID($page.params.bookid, (book) => (bookByID = book));
38
+ }
39
+ catch (err) {
40
+ error = '' + err;
41
+ }
42
+ }
43
+ // When page or auth changes and there's a user, update the listener.
44
+ $: if ($page && $auth && $auth.user !== null)
45
+ listen();
46
+ // When this is unmounted, unset them.
47
+ onDestroy(() => {
48
+ book.set(undefined);
49
+ unsub();
50
+ });
51
+ </script>
52
+
53
+ {#if error || currentBook === null}
54
+ <Feedback error>Unable to load book.</Feedback>
55
+ {:else if currentBook === undefined}
56
+ <Loading />
57
+ {:else}
58
+ <slot />
59
+ {/if}
@@ -0,0 +1,16 @@
1
+ import { SvelteComponentTyped } from "svelte";
2
+ declare const __propDef: {
3
+ props: Record<string, never>;
4
+ events: {
5
+ [evt: string]: CustomEvent<any>;
6
+ };
7
+ slots: {
8
+ default: {};
9
+ };
10
+ };
11
+ export type BookLoaderProps = typeof __propDef.props;
12
+ export type BookLoaderEvents = typeof __propDef.events;
13
+ export type BookLoaderSlots = typeof __propDef.slots;
14
+ export default class BookLoader extends SvelteComponentTyped<BookLoaderProps, BookLoaderEvents, BookLoaderSlots> {
15
+ }
16
+ export {};
@@ -1,10 +1,11 @@
1
1
  <script>import Instructions from './Instructions.svelte';
2
2
  import { getUniqueReferenceID, mineReference, } from '../../util/mineReference';
3
3
  import Reference from '../../models/book/Reference';
4
- import { getEdition } from './Contexts';
4
+ import { getEdition, isEditionEditable, isEditionPartiallyEditable, } from './Contexts';
5
5
  import Button from '../app/Button.svelte';
6
6
  let edition = getEdition();
7
7
  let text = '';
8
+ let editable = isEditionEditable() || isEditionPartiallyEditable();
8
9
  function handleBulkAdd() {
9
10
  // Split by lines, skipping empty lines.
10
11
  const references = text
@@ -24,17 +25,16 @@ function handleEmptyAdd() {
24
25
  }
25
26
  </script>
26
27
 
27
- <Instructions>
28
- To cite references in a chapter, add them here. You can add one at a time,
29
- or try pasting them in bulk, and we'll do our best to pull out the relevant
30
- parts, looking for things that look like author lists, years, sources, and
31
- URLs. Be sure to verify it though &mdash; it's likely to get some things
32
- wrong!
28
+ <Instructions {editable}>
29
+ To add references to a chapter, add them here first. You can add one at a
30
+ time, or try pasting them in bulk, and this page will try to pull out author
31
+ lists, years, sources, and URLs. Be sure to verify it though &mdash; it's
32
+ likely to get some things wrong.
33
33
  </Instructions>
34
34
 
35
35
  <textarea rows={5} bind:value={text} style="width: 100%" />
36
36
  <Button
37
- tooltip="convert text to references"
37
+ tooltip="convert to references"
38
38
  disabled={text.length === 0}
39
39
  command={handleBulkAdd}>+ bulk references</Button
40
40
  >