plainbook 0.0.6__py3-none-any.whl → 0.0.8__py3-none-any.whl
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.
- plainbook/js/AppNavbar.js +76 -0
- plainbook/js/CellInsertionZone.js +21 -0
- plainbook/js/CodeCell.js +2 -1
- plainbook/js/ExplanationEditor.js +5 -12
- plainbook/js/InfoModal.js +29 -0
- plainbook/js/MarkdownCell.js +1 -1
- plainbook/js/NotebookCell.js +72 -0
- plainbook/js/SettingsModal.js +46 -0
- plainbook/js/ValidationCell.js +2 -2
- plainbook/js/nb.js +23 -25
- plainbook/main.py +2 -1
- plainbook/views/index.html +82 -217
- {plainbook-0.0.6.dist-info → plainbook-0.0.8.dist-info}/METADATA +2 -2
- {plainbook-0.0.6.dist-info → plainbook-0.0.8.dist-info}/RECORD +18 -13
- {plainbook-0.0.6.dist-info → plainbook-0.0.8.dist-info}/WHEEL +0 -0
- {plainbook-0.0.6.dist-info → plainbook-0.0.8.dist-info}/entry_points.txt +0 -0
- {plainbook-0.0.6.dist-info → plainbook-0.0.8.dist-info}/licenses/LICENSE.md +0 -0
- {plainbook-0.0.6.dist-info → plainbook-0.0.8.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
props: ['isLocked', 'running', 'hasNotebook', 'lastRunIndex', 'cellCount', 'hasApiKey'],
|
|
3
|
+
emits: [
|
|
4
|
+
'lock', 'refresh', 'interrupt', 'regenerate-all',
|
|
5
|
+
'reset-run-all', 'open-info', 'open-settings'
|
|
6
|
+
],
|
|
7
|
+
template: /* html */ `
|
|
8
|
+
<nav class="navbar is-dark is-fixed-top" role="navigation" aria-label="main navigation">
|
|
9
|
+
<div id="the-navbar-menu" class="navbar-menu">
|
|
10
|
+
<div class="navbar-start">
|
|
11
|
+
<div class="navbar-item">
|
|
12
|
+
<div class="buttons">
|
|
13
|
+
<button v-if="isLocked" class="button is-warning" title="Unlock Notebook" @click="$emit('lock', false)">
|
|
14
|
+
<span class="icon"><i class="fa fa-lock"></i></span>
|
|
15
|
+
</button>
|
|
16
|
+
<button v-else class="button is-light" title="Lock Notebook" @click="$emit('lock', true)">
|
|
17
|
+
<span class="icon"><i class="fa fa-unlock"></i></span>
|
|
18
|
+
</button>
|
|
19
|
+
|
|
20
|
+
<button v-if="!running && hasNotebook"
|
|
21
|
+
@click="$emit('refresh')"
|
|
22
|
+
class="button is-light" title="Reload Notebook">
|
|
23
|
+
<span class="icon"><i class="fa fa-refresh"></i></span>
|
|
24
|
+
<span>Refresh</span>
|
|
25
|
+
</button>
|
|
26
|
+
|
|
27
|
+
<button v-if="running && hasNotebook"
|
|
28
|
+
@click="$emit('interrupt')"
|
|
29
|
+
class="button is-danger" title="Interrupt Execution">
|
|
30
|
+
<span class="icon"><i class="fa fa-stop"></i></span>
|
|
31
|
+
<span>Running...</span>
|
|
32
|
+
</button>
|
|
33
|
+
|
|
34
|
+
<button v-if="!running && hasNotebook && lastRunIndex >= cellCount - 1"
|
|
35
|
+
class="button is-light" title="All cells have been run">
|
|
36
|
+
<span class="icon"><i class="fa fa-check-circle"></i></span>
|
|
37
|
+
<span>Up to Date</span>
|
|
38
|
+
</button>
|
|
39
|
+
|
|
40
|
+
<button v-if="!running && hasNotebook"
|
|
41
|
+
:disabled="cellCount === 0 || isLocked"
|
|
42
|
+
@click="$emit('regenerate-all')"
|
|
43
|
+
title="Regenerate all code from descriptions"
|
|
44
|
+
class="button is-success">
|
|
45
|
+
<span class="icon"><i class="fa fa-repeat"></i></span>
|
|
46
|
+
<span>Regenerate All</span>
|
|
47
|
+
</button>
|
|
48
|
+
|
|
49
|
+
<button v-if="!running && hasNotebook"
|
|
50
|
+
:disabled="cellCount === 0"
|
|
51
|
+
@click="$emit('reset-run-all')"
|
|
52
|
+
title="Reset and run all cells"
|
|
53
|
+
class="button is-primary">
|
|
54
|
+
<span class="icon"><i class="fa fa-play"></i></span>
|
|
55
|
+
<span>Reset and Run All</span>
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</div>
|
|
60
|
+
<div class="navbar-end">
|
|
61
|
+
<div class="navbar-item">
|
|
62
|
+
<div class="buttons">
|
|
63
|
+
<button class="button is-light" @click="$emit('open-info')" title="About Plainbook">
|
|
64
|
+
<span class="icon"><i class="fa fa-info"></i></span>
|
|
65
|
+
</button>
|
|
66
|
+
<button class="button" :class="hasApiKey ? 'is-light' : 'is-warning'"
|
|
67
|
+
@click="$emit('open-settings')" title="Settings">
|
|
68
|
+
<span class="icon"><i :class="hasApiKey ? 'fa fa-cog' : 'fa fa-warning'"></i></span>
|
|
69
|
+
<span>Settings</span>
|
|
70
|
+
</button>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</nav>`
|
|
76
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// CellInsertionZone.js
|
|
2
|
+
export default {
|
|
3
|
+
props: [],
|
|
4
|
+
emits: ['insert'],
|
|
5
|
+
template: /* html */ `
|
|
6
|
+
<div class="cell-insert-zone">
|
|
7
|
+
<div class="cell-insert-buttons">
|
|
8
|
+
<button
|
|
9
|
+
class="button insert-cell is-info is-small py-0 px-3"
|
|
10
|
+
@click.stop="$emit('insert', 'markdown')">
|
|
11
|
+
Insert Comment
|
|
12
|
+
</button>
|
|
13
|
+
<button
|
|
14
|
+
class="button insert-cell is-info is-small py-0 px-3"
|
|
15
|
+
@click.stop="$emit('insert', 'code')">
|
|
16
|
+
Insert Action
|
|
17
|
+
</button>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
`
|
|
21
|
+
};
|
plainbook/js/CodeCell.js
CHANGED
|
@@ -128,7 +128,8 @@ export default {
|
|
|
128
128
|
if (!isCollapsed.value && isEditing.value) nextTick(autoResize);
|
|
129
129
|
};
|
|
130
130
|
|
|
131
|
-
return { isCollapsed, toggleCollapse, isEditing, cancelEdit, localSource,
|
|
131
|
+
return { isCollapsed, toggleCollapse, isEditing, cancelEdit, localSource,
|
|
132
|
+
localIsLocked, highlightedCode, enterEditMode, saveCode, textareaEl, autoResize, handleTabKey };
|
|
132
133
|
},
|
|
133
134
|
template: /* html */ `
|
|
134
135
|
<div class="code-cell-wrapper" style="position: relative; min-height: 1.75rem;">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ref, computed, watch, nextTick } from './vue.esm-browser.js';
|
|
2
2
|
|
|
3
3
|
const ExplanationRenderer = {
|
|
4
|
-
props: ['source', 'isActive', '
|
|
4
|
+
props: ['source', 'isActive', 'needsRunning', 'asRead', 'startEditKey', 'isLocked'],
|
|
5
5
|
emits: ['update:source', 'save', 'saveandrun', 'gencode', 'validate',
|
|
6
6
|
'run', 'delete', 'moveUp', 'moveDown'],
|
|
7
7
|
setup(props, { emit }) {
|
|
@@ -95,19 +95,11 @@ const ExplanationRenderer = {
|
|
|
95
95
|
<div class="toolbar-left">
|
|
96
96
|
<button class="button run-button is-small is-primary mr-1"
|
|
97
97
|
title="Run this cell and all necessary preceding cells" @click.stop="$emit('run')">
|
|
98
|
-
<
|
|
99
|
-
<span class="icon"><i class="fa fa-repeat"></i></span><span>Re-Run</span>
|
|
100
|
-
</template>
|
|
101
|
-
<template v-else-if="lastRunIndex < index">
|
|
102
|
-
<span class="icon"><i class="fa fa-step-forward"></i></span><span>Run Up To Here</span>
|
|
103
|
-
</template>
|
|
104
|
-
<template v-else>
|
|
105
|
-
<span class="icon"><i class="fa fa-step-forward"></i></span><span>Run From Start To Here</span>
|
|
106
|
-
</template>
|
|
98
|
+
<span class="icon"><i class="fa fa-step-forward"></i></span><span>Run</span>
|
|
107
99
|
</button>
|
|
108
100
|
<button class="button is-small" style="opacity: 0.6;">
|
|
109
101
|
<span v-if="asRead">Unmodified</span>
|
|
110
|
-
<span v-else-if="
|
|
102
|
+
<span v-else-if="needsRunning">Needs running</span>
|
|
111
103
|
<span v-else>Up to date</span>
|
|
112
104
|
</button>
|
|
113
105
|
</div>
|
|
@@ -159,7 +151,8 @@ const ExplanationRenderer = {
|
|
|
159
151
|
Save
|
|
160
152
|
</button>
|
|
161
153
|
<button class="button is-small is-primary" :disabled="localIsLocked" @click="saveAndRun">
|
|
162
|
-
|
|
154
|
+
<span class="icon"><i class="fa fa-play"></i></span>
|
|
155
|
+
<span>Save and Run</span>
|
|
163
156
|
</button>
|
|
164
157
|
</div>
|
|
165
158
|
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
props: ['isActive'],
|
|
3
|
+
emits: ['close'],
|
|
4
|
+
template: /* html */ `
|
|
5
|
+
<div class="modal" :class="{'is-active': isActive}">
|
|
6
|
+
<div class="modal-background" @click="$emit('close')"></div>
|
|
7
|
+
<div class="modal-card">
|
|
8
|
+
<header class="modal-card-head">
|
|
9
|
+
<p class="modal-card-title">Information</p>
|
|
10
|
+
<button class="delete" aria-label="close" @click="$emit('close')"></button>
|
|
11
|
+
</header>
|
|
12
|
+
<section class="modal-card-body">
|
|
13
|
+
<h1 class="title">Plainbook</h1>
|
|
14
|
+
<div class="content">
|
|
15
|
+
<p>Plainbook is an interactive notebook application for creating executable documents in natural language.</p>
|
|
16
|
+
<ul>
|
|
17
|
+
<li><strong>Action cells:</strong> Generate Python code from English explanations using AI.</li>
|
|
18
|
+
<li><strong>Markdown cells:</strong> Rich text for documentation.</li>
|
|
19
|
+
</ul>
|
|
20
|
+
<p>Locking prevents accidental edits while allowing validation and execution.</p>
|
|
21
|
+
<p><a href="https://github.com/lucadealfaro/plainbook" target="_blank">Plainbook Home Page</a></p>
|
|
22
|
+
</div>
|
|
23
|
+
</section>
|
|
24
|
+
<footer class="modal-card-foot">
|
|
25
|
+
<button class="button" @click="$emit('close')">Close</button>
|
|
26
|
+
</footer>
|
|
27
|
+
</div>
|
|
28
|
+
</div>`
|
|
29
|
+
};
|
plainbook/js/MarkdownCell.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ref, watch, nextTick } from './vue.esm-browser.js';
|
|
2
2
|
|
|
3
3
|
const MarkdownCell = {
|
|
4
|
-
props: ['source', 'startEditKey', 'isActive', '
|
|
4
|
+
props: ['source', 'startEditKey', 'isActive', 'isLocked'],
|
|
5
5
|
emits: ['save', 'delete', 'moveUp', 'moveDown'],
|
|
6
6
|
setup(props, { emit }) {
|
|
7
7
|
const md = new markdownit({ html: true });
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// NotebookCell.js
|
|
2
|
+
import MarkdownCell from './MarkdownCell.js';
|
|
3
|
+
import CodeCell from './CodeCell.js';
|
|
4
|
+
import ExplanationEditor from './ExplanationEditor.js';
|
|
5
|
+
import ValidationCell from './ValidationCell.js';
|
|
6
|
+
import OutputRenderer from './OutputRenderer.js';
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
components: { MarkdownCell, CodeCell, ExplanationEditor, ValidationCell, OutputRenderer },
|
|
10
|
+
props: ['cell', 'isActive', 'isLocked', 'needsRunning', 'asRead', 'markdownEditKey', 'explanationEditKey'],
|
|
11
|
+
emits: [
|
|
12
|
+
'save-markdown', 'save-explanation', 'save-code',
|
|
13
|
+
'run-cell', 'save-and-run', 'generate-code',
|
|
14
|
+
'validate-code', 'dismiss-validation',
|
|
15
|
+
'delete', 'move-up', 'move-down'
|
|
16
|
+
],
|
|
17
|
+
template: /* html */ `
|
|
18
|
+
<div class="notebook-cell box p-0 mb-2 is-clipped shadow-sm"
|
|
19
|
+
:style="{
|
|
20
|
+
border: isActive ? '2px solid #1d4ed8' : '1px solid transparent',
|
|
21
|
+
cursor: 'pointer'
|
|
22
|
+
}">
|
|
23
|
+
|
|
24
|
+
<markdown-cell
|
|
25
|
+
v-if="cell.cell_type === 'markdown'"
|
|
26
|
+
v-model:source="cell.source"
|
|
27
|
+
:is-active="isActive"
|
|
28
|
+
:start-edit-key="markdownEditKey"
|
|
29
|
+
:isLocked="isLocked"
|
|
30
|
+
@save="$emit('save-markdown', $event)"
|
|
31
|
+
@delete="$emit('delete')"
|
|
32
|
+
@moveUp="$emit('move-up')"
|
|
33
|
+
@moveDown="$emit('move-down')" />
|
|
34
|
+
|
|
35
|
+
<div v-else-if="cell.cell_type === 'code'">
|
|
36
|
+
<div v-if="cell.metadata?.explanation" class="has-background-light p-0 border-bottom">
|
|
37
|
+
<explanation-editor
|
|
38
|
+
v-model:source="cell.metadata.explanation"
|
|
39
|
+
:isActive="isActive"
|
|
40
|
+
:isLocked="isLocked"
|
|
41
|
+
:asRead="asRead"
|
|
42
|
+
:needs-running="needsRunning"
|
|
43
|
+
:start-edit-key="explanationEditKey"
|
|
44
|
+
@save="$emit('save-explanation', $event)"
|
|
45
|
+
@gencode="$emit('generate-code')"
|
|
46
|
+
@validate="$emit('validate-code')"
|
|
47
|
+
@run="$emit('run-cell')"
|
|
48
|
+
@saveandrun="$emit('save-and-run', $event)"
|
|
49
|
+
@delete="$emit('delete')"
|
|
50
|
+
@moveUp="$emit('move-up')"
|
|
51
|
+
@moveDown="$emit('move-down')" />
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<validation-cell
|
|
55
|
+
v-if="cell.metadata?.validation && !cell.metadata?.validation.is_hidden"
|
|
56
|
+
:validation="cell.metadata.validation"
|
|
57
|
+
@dismiss_validation="$emit('dismiss-validation')" />
|
|
58
|
+
|
|
59
|
+
<code-cell
|
|
60
|
+
v-model:source="cell.source"
|
|
61
|
+
:execution-count="cell.execution_count"
|
|
62
|
+
:is-active="isActive"
|
|
63
|
+
:is-locked="isLocked"
|
|
64
|
+
@save="$emit('save-code', $event)" />
|
|
65
|
+
|
|
66
|
+
<div v-if="cell.outputs?.length" class="p-2 border-top has-background-white">
|
|
67
|
+
<output-renderer v-for="(out, oIdx) in cell.outputs" :key="oIdx" :output="out" />
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
`
|
|
72
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ref, watch } from './vue.esm-browser.js';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
props: ['isActive', 'apiKey'],
|
|
5
|
+
emits: ['close', 'save'],
|
|
6
|
+
setup(props, { emit }) {
|
|
7
|
+
// Create a local draft of the API key
|
|
8
|
+
const localKey = ref(props.apiKey);
|
|
9
|
+
|
|
10
|
+
// Sync local draft whenever the modal is opened with the current parent value
|
|
11
|
+
watch(() => props.isActive, (active) => {
|
|
12
|
+
if (active) {
|
|
13
|
+
localKey.value = props.apiKey;
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const handleSave = () => {
|
|
18
|
+
emit('save', localKey.value);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
return { localKey, handleSave };
|
|
22
|
+
},
|
|
23
|
+
template: /* html */ `
|
|
24
|
+
<div class="modal" :class="{'is-active': isActive}">
|
|
25
|
+
<div class="modal-background" @click="$emit('close')"></div>
|
|
26
|
+
<div class="modal-card">
|
|
27
|
+
<header class="modal-card-head">
|
|
28
|
+
<p class="modal-card-title">Settings</p>
|
|
29
|
+
<button class="delete" aria-label="close" @click="$emit('close')"></button>
|
|
30
|
+
</header>
|
|
31
|
+
<section class="modal-card-body">
|
|
32
|
+
<div class="field">
|
|
33
|
+
<label class="label">Gemini API Key</label>
|
|
34
|
+
<div class="control">
|
|
35
|
+
<input class="input" type="text"
|
|
36
|
+
v-model="localKey"
|
|
37
|
+
placeholder="Enter your Gemini API key">
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</section>
|
|
41
|
+
<footer class="modal-card-foot" style="justify-content: flex-end;">
|
|
42
|
+
<button class="button is-primary" @click="handleSave">Save</button>
|
|
43
|
+
</footer>
|
|
44
|
+
</div>
|
|
45
|
+
</div>`
|
|
46
|
+
};
|
plainbook/js/ValidationCell.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {ref, watch, nextTick, computed} from './vue.esm-browser.js';
|
|
2
2
|
|
|
3
3
|
export default {
|
|
4
|
-
props: ['validation'
|
|
4
|
+
props: ['validation'],
|
|
5
5
|
emits: ['dismiss_validation'],
|
|
6
6
|
|
|
7
7
|
setup(props, { emit }) {
|
|
@@ -25,7 +25,7 @@ export default {
|
|
|
25
25
|
if (is_hidden.value) {
|
|
26
26
|
is_hidden.value = true;
|
|
27
27
|
}
|
|
28
|
-
emit('dismiss_validation'
|
|
28
|
+
emit('dismiss_validation');
|
|
29
29
|
};
|
|
30
30
|
return { dismiss, renderedMarkdown, message, is_valid, is_hidden };
|
|
31
31
|
},
|
plainbook/js/nb.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { createApp, ref, onMounted, onBeforeUnmount, nextTick, getCurrentInstance } from './vue.esm-browser.js';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
3
|
+
import AppNavbar from './AppNavbar.js';
|
|
4
|
+
import NotebookCell from './NotebookCell.js';
|
|
5
|
+
import CellInsertionZone from './CellInsertionZone.js';
|
|
6
|
+
import SettingsModal from './SettingsModal.js';
|
|
7
|
+
import InfoModal from './InfoModal.js';
|
|
8
8
|
|
|
9
9
|
createApp({
|
|
10
|
-
components: {
|
|
10
|
+
components: { AppNavbar, NotebookCell, CellInsertionZone, SettingsModal, InfoModal },
|
|
11
11
|
setup() {
|
|
12
12
|
// Extract token from URL
|
|
13
13
|
const urlParams = new URLSearchParams(window.location.search);
|
|
@@ -152,6 +152,9 @@ createApp({
|
|
|
152
152
|
};
|
|
153
153
|
|
|
154
154
|
const generateCode = async (cellIndex) => {
|
|
155
|
+
if (!geminiApiKey.value) {
|
|
156
|
+
throw new Error('Gemini API key is not set. Please set it in the settings.');
|
|
157
|
+
};
|
|
155
158
|
asRead.value = false;
|
|
156
159
|
try {
|
|
157
160
|
const response = await fetch(`/generate_code?token=${authToken}`, {
|
|
@@ -172,6 +175,9 @@ createApp({
|
|
|
172
175
|
};
|
|
173
176
|
|
|
174
177
|
const regenerateAllCode = async () => {
|
|
178
|
+
if (!geminiApiKey.value) {
|
|
179
|
+
throw new Error('Gemini API key is not set. Please set it in the settings.');
|
|
180
|
+
};
|
|
175
181
|
for (let i = 0; i < notebook.value.cells.length; i++) {
|
|
176
182
|
if (notebook.value.cells[i].cell_type === 'code') {
|
|
177
183
|
await generateCode(i);
|
|
@@ -185,6 +191,9 @@ createApp({
|
|
|
185
191
|
};
|
|
186
192
|
|
|
187
193
|
const validateCode = async (cellIndex) => {
|
|
194
|
+
if (!geminiApiKey.value) {
|
|
195
|
+
throw new Error('Gemini API key is not set. Please set it in the settings.');
|
|
196
|
+
};
|
|
188
197
|
asRead.value = false;
|
|
189
198
|
try {
|
|
190
199
|
const response = await fetch(`/validate_code?token=${authToken}`, {
|
|
@@ -386,6 +395,8 @@ createApp({
|
|
|
386
395
|
const r = await response.json();
|
|
387
396
|
if (r.status === 'error') {
|
|
388
397
|
throw new Error(r.message || 'Execution failed');
|
|
398
|
+
} else if (r.details !== 'ok') {
|
|
399
|
+
throw new Error('Cell execution error');
|
|
389
400
|
} else {
|
|
390
401
|
console.log('Cell executed:', cellIndex, r.details);
|
|
391
402
|
// Update outputs in the notebook model
|
|
@@ -396,9 +407,11 @@ createApp({
|
|
|
396
407
|
if (r.last_executed_cell !== undefined && r.last_executed_cell !== null) {
|
|
397
408
|
lastRunIndex.value = r.last_executed_cell;
|
|
398
409
|
}
|
|
410
|
+
//
|
|
399
411
|
}
|
|
400
412
|
} catch (err) {
|
|
401
|
-
|
|
413
|
+
running.value = false; // No longer running.
|
|
414
|
+
throw new Error(err.message);
|
|
402
415
|
}
|
|
403
416
|
};
|
|
404
417
|
|
|
@@ -452,13 +465,7 @@ createApp({
|
|
|
452
465
|
}
|
|
453
466
|
};
|
|
454
467
|
|
|
455
|
-
const
|
|
456
|
-
// Get the Gemini API key from the server settings.
|
|
457
|
-
|
|
458
|
-
showSettings.value = true;
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
const closeSettings = async () => {
|
|
468
|
+
const saveSettings = async (newKey) => {
|
|
462
469
|
// Save the Gemini API key to the server
|
|
463
470
|
try {
|
|
464
471
|
const response = await fetch(`/set_key?token=${authToken}`, {
|
|
@@ -474,21 +481,13 @@ createApp({
|
|
|
474
481
|
} catch (err) {
|
|
475
482
|
throw new Error('Error saving API key: ' + err.message);
|
|
476
483
|
}
|
|
477
|
-
|
|
484
|
+
geminiApiKey.value = newKey;
|
|
478
485
|
};
|
|
479
486
|
|
|
480
487
|
const genError = () => {
|
|
481
488
|
throw new Error('This is a generated error for testing purposes. This is a generated error for testing purposes. This is a generated error for testing purposes. This is a generated error for testing purposes. ');
|
|
482
489
|
}
|
|
483
490
|
|
|
484
|
-
const openInfo = () => {
|
|
485
|
-
showInfo.value = true;
|
|
486
|
-
};
|
|
487
|
-
|
|
488
|
-
const closeInfo = () => {
|
|
489
|
-
showInfo.value = false;
|
|
490
|
-
};
|
|
491
|
-
|
|
492
491
|
const closeUiError = () => {
|
|
493
492
|
uiError.value = null;
|
|
494
493
|
};
|
|
@@ -519,8 +518,7 @@ createApp({
|
|
|
519
518
|
validateCode, dismissValidation, resetAndRunAllCells,
|
|
520
519
|
setActiveCell, runCell, running, lastRunIndex, asRead, runAllCells,
|
|
521
520
|
interruptKernel, insertCell, markdownEditKey,
|
|
522
|
-
|
|
523
|
-
openInfo, closeInfo, showInfo,
|
|
521
|
+
saveSettings, showSettings, showInfo,
|
|
524
522
|
genError, uiError, closeUiError,
|
|
525
523
|
explanationEditKey, deleteCell, moveCell, geminiApiKey };
|
|
526
524
|
},
|
plainbook/main.py
CHANGED
|
@@ -183,7 +183,8 @@ def execute_cell():
|
|
|
183
183
|
print(f"Executing cell {cell_index}")
|
|
184
184
|
try:
|
|
185
185
|
outputs, details = notebook.execute_cell(cell_index)
|
|
186
|
-
return dict(status="ok", details=details,
|
|
186
|
+
return dict(status="ok", details=details,
|
|
187
|
+
outputs=outputs, last_executed_cell=notebook.last_executed_cell)
|
|
187
188
|
except CellExecutionError as e:
|
|
188
189
|
# The execution error is already captured in the cell outputs.
|
|
189
190
|
return dict(status="ok", details="CellExecutionError",
|
plainbook/views/index.html
CHANGED
|
@@ -29,235 +29,100 @@
|
|
|
29
29
|
</style>
|
|
30
30
|
</head>
|
|
31
31
|
<body class="has-navbar-fixed-top">
|
|
32
|
-
|
|
33
|
-
<div id="app"></div>
|
|
34
32
|
|
|
35
33
|
<script type="text/x-template" id="app-template">
|
|
36
|
-
<nav class="navbar is-dark is-fixed-top" role="navigation" aria-label="main navigation">
|
|
37
|
-
<div id="the-navbar-menu" class="navbar-menu">
|
|
38
|
-
<div class="navbar-start">
|
|
39
|
-
<div class="navbar-item">
|
|
40
|
-
<div class="buttons">
|
|
41
|
-
<!-- Locking -->
|
|
42
|
-
<button v-if="isLocked" class="button is-warning" title="Unlock Notebook" @click="lockNotebook(false)">
|
|
43
|
-
<span class="icon"><i class="fa fa-lock"></i></span>
|
|
44
|
-
</button>
|
|
45
|
-
<button v-else class="button is-light" title="Lock Notebook" @click="lockNotebook(true)">
|
|
46
|
-
<span class="icon"><i class="fa fa-unlock"></i></span>
|
|
47
|
-
</button>
|
|
48
|
-
<!-- Reload notebook -->
|
|
49
|
-
<button v-if="!running && notebook"
|
|
50
|
-
@click="reloadNotebook"
|
|
51
|
-
class="button is-light" title="Reload Notebook">
|
|
52
|
-
<span class="icon"><i class="fa fa-refresh"></i></span>
|
|
53
|
-
<span>Refresh</span>
|
|
54
|
-
</button>
|
|
55
|
-
<!-- Interrupt execution -->
|
|
56
|
-
<button v-if="running && notebook"
|
|
57
|
-
@click="interruptKernel"
|
|
58
|
-
class="button is-danger" title="Interrupt Execution">
|
|
59
|
-
<span class="icon"><i class="fa fa-stop"></i></span>
|
|
60
|
-
<span>Running...</span>
|
|
61
|
-
</button>
|
|
62
|
-
<!-- Status -->
|
|
63
|
-
<button v-if="!running && notebook && lastRunIndex >= notebook.cells.length - 1"
|
|
64
|
-
class="button is-light" title="All cells have been run">
|
|
65
|
-
<span class="icon"><i class="fa fa-check-circle"></i></span>
|
|
66
|
-
<span>Up to Date</span>
|
|
67
|
-
</button>
|
|
68
|
-
<!-- Running -->
|
|
69
|
-
<!-- <button v-if="!running && notebook"
|
|
70
|
-
:disabled="notebook.cells.length === 0 || isLocked"
|
|
71
|
-
@click="regenerateAndRunAllCode" title="Regenerate all code from descriptions and run it all"
|
|
72
|
-
class="button is-success">
|
|
73
|
-
<span class="icon"><i class="fa fa-repeat"></i></span>
|
|
74
|
-
<span class="icon"><i class="fa fa-play"></i></span>
|
|
75
|
-
</button> -->
|
|
76
|
-
<button v-if="!running && notebook"
|
|
77
|
-
:disabled="notebook.cells.length === 0 || isLocked"
|
|
78
|
-
@click="regenerateAllCode" title="Regenerate all code from descriptions"
|
|
79
|
-
class="button is-success">
|
|
80
|
-
<span class="icon"><i class="fa fa-repeat"></i></span>
|
|
81
|
-
<span>Regenerate All</span>
|
|
82
|
-
</button>
|
|
83
|
-
<button v-if="!running && notebook"
|
|
84
|
-
:disabled="notebook.cells.length === 0"
|
|
85
|
-
@click="resetAndRunAllCells" title="Reset and run all cells"
|
|
86
|
-
class="button is-primary">
|
|
87
|
-
<span class="icon"><i class="fa fa-play"></i></span>
|
|
88
|
-
<span>Reset and Run All</span>
|
|
89
|
-
</button>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
93
|
-
<div class="navbar-end">
|
|
94
|
-
<div class="navbar-item">
|
|
95
|
-
<div class="buttons">
|
|
96
|
-
<!-- <button id="btn-info" class="button is-danger" @click="genError">
|
|
97
|
-
<span class="icon"><i class="fa fa-exclamation"></i></span>
|
|
98
|
-
</button> -->
|
|
99
|
-
<button id="btn-info" class="button is-light" @click="openInfo" title="About Plainbook">
|
|
100
|
-
<span class="icon"><i class="fa fa-info"></i></span>
|
|
101
|
-
</button>
|
|
102
|
-
<button id="btn-settings" class="button is-light" @click="openSettings" title="Settings">
|
|
103
|
-
<span class="icon"><i :class="geminiApiKey ? 'fa fa-cog' : 'fa fa-warning'"></i></span>
|
|
104
|
-
<span>Settings</span>
|
|
105
|
-
</button>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
</div>
|
|
110
|
-
</nav>
|
|
111
34
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
35
|
+
<app-navbar
|
|
36
|
+
:is-locked="isLocked"
|
|
37
|
+
:running="running"
|
|
38
|
+
:has-notebook="!!notebook"
|
|
39
|
+
:last-run-index="lastRunIndex"
|
|
40
|
+
:cell-count="notebook ? notebook.cells.length : 0"
|
|
41
|
+
:has-api-key="!!geminiApiKey"
|
|
42
|
+
@lock="lockNotebook"
|
|
43
|
+
@refresh="reloadNotebook"
|
|
44
|
+
@interrupt="interruptKernel"
|
|
45
|
+
@regenerate-all="regenerateAllCode"
|
|
46
|
+
@reset-run-all="resetAndRunAllCells"
|
|
47
|
+
@open-info="showInfo = true"
|
|
48
|
+
@open-settings="showSettings = true"
|
|
49
|
+
/>
|
|
117
50
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
{{
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
<
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
<button class="
|
|
51
|
+
<div v-if="uiError" class="notification mb-0 mt-0 px-4 pl-2 pr-6 has-text-danger has-background-danger-light"
|
|
52
|
+
style="width: 100%; border-radius: 0; align-items: center; justify-content: space-between;">
|
|
53
|
+
<span><strong>Error:</strong> {{ uiError }}</span>
|
|
54
|
+
<button class="delete" @click="closeUiError"></button>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<div class="section notebook-area">
|
|
58
|
+
<h1 class="title">
|
|
59
|
+
{{ notebook_name }}
|
|
60
|
+
</h1>
|
|
61
|
+
<div class="notebook-container px-2 py-2">
|
|
62
|
+
<div v-if="loading" class="has-text-centered py-6">
|
|
63
|
+
<button class="button is-loading is-ghost is-large">Loading</button>
|
|
64
|
+
<p class="has-text-grey">Fetching notebook data...</p>
|
|
65
|
+
</div>
|
|
66
|
+
<div v-else-if="error" class="notification is-danger is-light">
|
|
67
|
+
<button class="delete" @click="error = null"></button>
|
|
68
|
+
<strong>Error:</strong> {{ error }}
|
|
135
69
|
</div>
|
|
136
|
-
<
|
|
137
|
-
<!-- Insert zone before each cell -->
|
|
138
|
-
<div v-if="!isLocked" class="cell-insert-zone">
|
|
139
|
-
<div class="cell-insert-buttons">
|
|
140
|
-
<button class="button insert-cell is-info is-small py-0 px-3" @click.stop="insertCell(index, 'markdown')">Insert Comment</button>
|
|
141
|
-
<button class="button insert-cell is-info is-small py-0 px-3" @click.stop="insertCell(index, 'code')">Insert Action</button>
|
|
142
|
-
</div>
|
|
143
|
-
</div>
|
|
70
|
+
<div v-else="notebook">
|
|
144
71
|
|
|
145
|
-
<
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
border: activeIndex === index ? '2px solid #1d4ed8' : '1px solid transparent',
|
|
149
|
-
cursor: 'pointer'
|
|
150
|
-
}">
|
|
151
|
-
<markdown-cell v-if="cell.cell_type === 'markdown'" :source="cell.source"
|
|
152
|
-
:is-active="activeIndex === index"
|
|
153
|
-
:start-edit-key="markdownEditKey[index]"
|
|
154
|
-
:index="index"
|
|
155
|
-
:isLocked="isLocked"
|
|
156
|
-
@save="(content) => sendMarkdownToServer(content, index)"
|
|
157
|
-
@delete="deleteCell(index)"
|
|
158
|
-
@moveUp="moveCell(index, -1)"
|
|
159
|
-
@moveDown="moveCell(index, 1)" />
|
|
72
|
+
<cell-insertion-zone v-if="!isLocked"
|
|
73
|
+
@insert="(celltype) => insertCell(0, celltype)"
|
|
74
|
+
/>
|
|
160
75
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
@saveandrun="(content) => saveExplanationAndRun (content, index)"
|
|
174
|
-
@delete="deleteCell(index)"
|
|
175
|
-
@moveUp="moveCell(index, -1)"
|
|
176
|
-
@moveDown="moveCell(index, 1)" />
|
|
177
|
-
</div>
|
|
178
|
-
<div v-if="cell.metadata?.validation && !cell.metadata?.validation.is_hidden">
|
|
179
|
-
<validation-cell :validation="cell.metadata.validation"
|
|
180
|
-
:index="index"
|
|
181
|
-
@dismiss_validation="dismissValidation" />
|
|
182
|
-
</div>
|
|
183
|
-
<code-cell :source="cell.source"
|
|
184
|
-
:execution-count="cell.execution_count"
|
|
185
|
-
:is-active="activeIndex === index"
|
|
186
|
-
:is-locked="isLocked"
|
|
187
|
-
@save="(content) => sendCodeToServer(content, index)" />
|
|
76
|
+
<template v-for="(cell, index) in notebook.cells" :key="index">
|
|
77
|
+
|
|
78
|
+
<notebook-cell
|
|
79
|
+
:cell="cell"
|
|
80
|
+
:is-active="activeIndex === index"
|
|
81
|
+
:needs-running="index > lastRunIndex"
|
|
82
|
+
:is-locked="isLocked"
|
|
83
|
+
:last-run-index="lastRunIndex"
|
|
84
|
+
:as-read="asRead"
|
|
85
|
+
:markdown-edit-key="markdownEditKey[index]"
|
|
86
|
+
:explanation-edit-key="explanationEditKey[index]"
|
|
87
|
+
@click="setActiveCell(index)"
|
|
188
88
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
</div>
|
|
89
|
+
@save-markdown="(content) => sendMarkdownToServer(content, index)"
|
|
90
|
+
@save-explanation="(content) => sendExplanationToServer(content, index)"
|
|
91
|
+
@save-code="(content) => sendCodeToServer(content, index)"
|
|
92
|
+
@save-and-run="(content) => saveExplanationAndRun(content, index)"
|
|
93
|
+
|
|
94
|
+
@run-cell="runCell(index)"
|
|
95
|
+
@generate-code="generateCode(index)"
|
|
96
|
+
@validate-code="validateCode(index)"
|
|
97
|
+
@dismiss-validation="dismissValidation(index)"
|
|
98
|
+
|
|
99
|
+
@delete="deleteCell(index)"
|
|
100
|
+
@move-up="moveCell(index, -1)"
|
|
101
|
+
@move-down="moveCell(index, 1)"
|
|
102
|
+
/>
|
|
206
103
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
<div class="modal-card">
|
|
211
|
-
<header class="modal-card-head">
|
|
212
|
-
<p class="modal-card-title">Settings</p>
|
|
213
|
-
<button class="delete" aria-label="close" @click="closeSettings"></button>
|
|
214
|
-
</header>
|
|
215
|
-
<section class="modal-card-body">
|
|
216
|
-
<div class="field">
|
|
217
|
-
<label class="label">Gemini API Key</label>
|
|
218
|
-
<div class="control">
|
|
219
|
-
<input class="input" type="text" v-model="geminiApiKey" placeholder="Enter your Gemini API key">
|
|
220
|
-
</div>
|
|
221
|
-
<p class="help">Your API key is stored locally in ~/.settings/plainbook and used for AI-powered features.</p>
|
|
222
|
-
</div>
|
|
223
|
-
</section>
|
|
224
|
-
<footer class="modal-card-foot">
|
|
225
|
-
<button class="button" @click="closeSettings">Close</button>
|
|
226
|
-
</footer>
|
|
227
|
-
</div>
|
|
228
|
-
</div>
|
|
104
|
+
<cell-insertion-zone v-if="!isLocked"
|
|
105
|
+
@insert="(celltype) => insertCell(index + 1, celltype)"
|
|
106
|
+
/>
|
|
229
107
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
<div class="modal-background" @click="closeInfo"></div>
|
|
233
|
-
<div class="modal-card">
|
|
234
|
-
<header class="modal-card-head">
|
|
235
|
-
<p class="modal-card-title">Information</p>
|
|
236
|
-
<button class="delete" aria-label="close" @click="closeInfo"></button>
|
|
237
|
-
</header>
|
|
238
|
-
<section class="modal-card-body">
|
|
239
|
-
<h1 class="title">Plainbook</h1>
|
|
240
|
-
<div class="content">
|
|
241
|
-
<p>Plainbook is an interactive notebook application for creating executable doocuments in
|
|
242
|
-
natural language. A notebook consists of:</p>
|
|
243
|
-
<ul>
|
|
244
|
-
<li><strong>Action cells</strong> that contain an English explanation. The explanation is
|
|
245
|
-
used to generate Python code using an AI model. The generated code is then executed,
|
|
246
|
-
and the output is displayed below the cell.</li>
|
|
247
|
-
<li><strong>Markdown cells</strong> for writing explanations, comments, and documentation in rich text format.</li>
|
|
248
|
-
</ul>
|
|
249
|
-
<p>If you lock a notebook, editing of cells and generation of code will be disabled
|
|
250
|
-
to prevent accidental changes. However, you can still validate the code against
|
|
251
|
-
its description, and you can still run the notebook.
|
|
252
|
-
Unlock it to be able to edit again.</p>
|
|
253
|
-
<p><a href="https://github.com/lucadealfaro/plainbook" target="_blank">Plainbook Home Page</a></p>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
254
110
|
</div>
|
|
255
|
-
</
|
|
256
|
-
<footer class="modal-card-foot">
|
|
257
|
-
<button class="button" @click="closeInfo">Close</button>
|
|
258
|
-
</footer>
|
|
111
|
+
</div>
|
|
259
112
|
</div>
|
|
260
|
-
|
|
113
|
+
|
|
114
|
+
<!-- Settings Modal -->
|
|
115
|
+
<settings-modal
|
|
116
|
+
:is-active="showSettings"
|
|
117
|
+
:api-key="geminiApiKey"
|
|
118
|
+
@close="showSettings = false"
|
|
119
|
+
@save="(newKey) => {showSettings = false; saveSettings(newKey); }"
|
|
120
|
+
/>
|
|
121
|
+
<!-- Info Modal -->
|
|
122
|
+
<info-modal
|
|
123
|
+
:is-active="showInfo"
|
|
124
|
+
@close="showInfo = false"
|
|
125
|
+
/>
|
|
261
126
|
|
|
262
127
|
</script>
|
|
263
128
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: plainbook
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.8
|
|
4
4
|
Summary: Plain Language Notebooks
|
|
5
5
|
Author-email: Luca de Alfaro <dealfaro@acm.org>
|
|
6
|
-
License: BSD-3-Clause
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
7
|
Project-URL: Homepage, https://github.com/lucadealfaro/plainbook
|
|
8
8
|
Project-URL: Issues, https://github.com/lucadealfaro/plainbook/issues
|
|
9
9
|
Keywords: notebook,jupyter,natural language,data science
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
plainbook/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
2
2
|
plainbook/gemini.py,sha256=4lgvMdQdcKyMZLSnu9yhwlaI4LaSvvyr_-SA0DdI628,2915
|
|
3
|
-
plainbook/main.py,sha256=
|
|
3
|
+
plainbook/main.py,sha256=XruVZYj-nQ-2V9wiGKO7AV0-Via0Rz2CUafuX1id3J8,10172
|
|
4
4
|
plainbook/plainbook.py,sha256=zgi3e50r9vFfMLxXuMVcvyB9I7yEJOgZthxnz3RxuKw,17063
|
|
5
5
|
plainbook/css/font-awesome.css,sha256=NuCn4IvuZXdBaFKJOAcsU2Q3ZpwbdFisd5dux4jkQ5w,37414
|
|
6
6
|
plainbook/css/font-awesome.min.css,sha256=eZrrJcwDc_3uDhsdt61sL2oOBY362qM3lon1gyExkL0,31000
|
|
@@ -12,20 +12,25 @@ plainbook/fonts/fontawesome-webfont.svg,sha256=rWFXkmwWIrpOHQPUePFUE2hSS_xG9R5C_
|
|
|
12
12
|
plainbook/fonts/fontawesome-webfont.ttf,sha256=qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg,165548
|
|
13
13
|
plainbook/fonts/fontawesome-webfont.woff,sha256=ugxZ3rVFD1y0Gz-TYJ7i0NmVQVh33foiPoqKdTNHTwc,98024
|
|
14
14
|
plainbook/fonts/fontawesome-webfont.woff2,sha256=Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8_4,77160
|
|
15
|
-
plainbook/js/
|
|
16
|
-
plainbook/js/
|
|
17
|
-
plainbook/js/
|
|
15
|
+
plainbook/js/AppNavbar.js,sha256=nEX2CXdI0136Usj-YVts6QXV4xd_vbsXh9-mPvgbi5E,3968
|
|
16
|
+
plainbook/js/CellInsertionZone.js,sha256=XPo2hmuG9lfVzYbZ1bBO-44HiyCFngHzxoUa56v42s8,676
|
|
17
|
+
plainbook/js/CodeCell.js,sha256=cK6YOKVu58g4h7KmH2aL3LA99oxe8Z3ZZpLRF0k6XZY,6943
|
|
18
|
+
plainbook/js/ExplanationEditor.js,sha256=FDflmKZKzNHkKgXHwsYrbQDLDK1ROVg00-5p9cj2jlc,7088
|
|
19
|
+
plainbook/js/InfoModal.js,sha256=ShylUmDQdQ-2g-z6PlvZ_TTlxCSdNGJJICu-RAAwab8,1442
|
|
20
|
+
plainbook/js/MarkdownCell.js,sha256=SxU_LO4TGcxOEy6sKognF_gHUXwmqR6BDsv4z0tlgVQ,5292
|
|
21
|
+
plainbook/js/NotebookCell.js,sha256=PNWdd6VSWtD1nHeUCHPEZWIwBMQT26rjN6-P0rPCEZI,3239
|
|
18
22
|
plainbook/js/OutputRenderer.js,sha256=KXECI2xd0Sk_TnZnAfqfhL0dEIT9pjZZTXo6Qdiw0o4,2981
|
|
19
|
-
plainbook/js/
|
|
23
|
+
plainbook/js/SettingsModal.js,sha256=HpU2gKj26zZHhilcfp3hghEsW-sxsgq1HBlcsIBDGCI,1689
|
|
24
|
+
plainbook/js/ValidationCell.js,sha256=4XdlBNELTpeARiNVh55_7ay24CEY-aUNJy1knX8I9MU,1595
|
|
20
25
|
plainbook/js/markdown-it.min.js,sha256=hNyljag6giCsjv_yKmxK8_VeHzvMDvc5u8AzmRvm1BI,103012
|
|
21
|
-
plainbook/js/nb.js,sha256=
|
|
26
|
+
plainbook/js/nb.js,sha256=CZs32sAevGsea7PUjJK0Prd-7_mz1G82_S9_Hpx5gmQ,22822
|
|
22
27
|
plainbook/js/prism-python.min.js,sha256=7UOFaFvPLUk1yNu6tL3hZgPaEyngktK_NsPa3WfpqFw,2113
|
|
23
28
|
plainbook/js/prism.min.js,sha256=57iL3cbHV7L8jLET4kaYAasUp47BqPraTWOR41c_X58,18997
|
|
24
29
|
plainbook/js/vue.esm-browser.js,sha256=75FuLhUTPk19sncwNIrm0BGEL0_Qw298-_v01fPWYoI,542872
|
|
25
|
-
plainbook/views/index.html,sha256=
|
|
26
|
-
plainbook-0.0.
|
|
27
|
-
plainbook-0.0.
|
|
28
|
-
plainbook-0.0.
|
|
29
|
-
plainbook-0.0.
|
|
30
|
-
plainbook-0.0.
|
|
31
|
-
plainbook-0.0.
|
|
30
|
+
plainbook/views/index.html,sha256=2LEoKQMoAuCwDSTg8kpstvINJUL33QTcfLkEspS1wYI,5557
|
|
31
|
+
plainbook-0.0.8.dist-info/licenses/LICENSE.md,sha256=JW6Hp9mBeCkRkCSeiT2KmIU3XA5mJorjcIlM2cOOxyU,1462
|
|
32
|
+
plainbook-0.0.8.dist-info/METADATA,sha256=oqeZaTNU720_qAR6HqaiCu6Kvmpd7pj8ofV1hxkvOvo,3023
|
|
33
|
+
plainbook-0.0.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
34
|
+
plainbook-0.0.8.dist-info/entry_points.txt,sha256=fktud-zRh9ZZ4rXv5w6kW-78u44lSYklXY1ttMTB9k8,50
|
|
35
|
+
plainbook-0.0.8.dist-info/top_level.txt,sha256=Y3jOV2n79dczNw9tJ7acufz3y3ns2pWw8b06sIN4ltc,10
|
|
36
|
+
plainbook-0.0.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|