solara-ui 1.38.0__py2.py3-none-any.whl → 1.40.0__py2.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.
- solara/__init__.py +2 -2
- solara/__main__.py +8 -1
- solara/checks.py +4 -1
- solara/components/__init__.py +1 -0
- solara/components/echarts.py +8 -0
- solara/components/echarts.vue +1 -1
- solara/components/input_text_area.py +86 -0
- solara/components/markdown.py +10 -1
- solara/components/markdown_editor.py +8 -0
- solara/components/markdown_editor.vue +1 -1
- solara/components/sql_code.py +8 -0
- solara/components/sql_code.vue +1 -1
- solara/hooks/use_thread.py +4 -4
- solara/lab/components/chat.py +8 -2
- solara/server/assets/style.css +2 -1
- solara/server/flask.py +1 -1
- solara/server/jupyter/server_extension.py +11 -1
- solara/server/jupyter/solara.py +91 -0
- solara/server/patch.py +1 -0
- solara/server/pyinstaller/__init__.py +9 -0
- solara/server/pyinstaller/hook-ipyreact.py +5 -0
- solara/server/pyinstaller/hook-ipyvuetify.py +5 -0
- solara/server/pyinstaller/hook-solara.py +9 -0
- solara/server/server.py +6 -1
- solara/server/starlette.py +18 -6
- solara/server/static/highlight-dark.css +1 -1
- solara/server/static/main-vuetify.js +1 -1
- solara/server/static/solara_bootstrap.py +1 -1
- solara/server/templates/solara.html.j2 +30 -6
- solara/website/assets/custom.css +20 -57
- solara/website/components/__init__.py +2 -2
- solara/website/components/algolia_api.vue +23 -6
- solara/website/components/breadcrumbs.py +28 -0
- solara/website/components/contact.py +144 -0
- solara/website/components/docs.py +11 -9
- solara/website/components/header.py +31 -20
- solara/website/components/markdown.py +12 -1
- solara/website/components/markdown_nav.vue +34 -0
- solara/website/components/sidebar.py +7 -1
- solara/website/pages/__init__.py +87 -254
- solara/website/pages/about/__init__.py +9 -0
- solara/website/pages/about/about.md +3 -0
- solara/website/pages/careers/__init__.py +27 -0
- solara/website/pages/changelog/__init__.py +2 -2
- solara/website/pages/changelog/changelog.md +23 -0
- solara/website/pages/contact/__init__.py +30 -6
- solara/website/pages/documentation/__init__.py +25 -33
- solara/website/pages/documentation/advanced/content/10-howto/40-embed.md +2 -1
- solara/website/pages/documentation/advanced/content/15-reference/41-asset-files.md +1 -1
- solara/website/pages/documentation/advanced/content/20-understanding/50-solara-server.md +2 -1
- solara/website/pages/documentation/advanced/content/30-enterprise/00-overview.md +1 -1
- solara/website/pages/documentation/advanced/content/30-enterprise/10-oauth.md +5 -2
- solara/website/pages/documentation/api/hooks/use_thread.md +6 -0
- solara/website/pages/documentation/components/data/pivot_table.py +2 -2
- solara/website/pages/documentation/components/input/input.py +2 -0
- solara/website/pages/documentation/components/output/sql_code.py +3 -3
- solara/website/pages/documentation/examples/__init__.py +2 -2
- solara/website/pages/documentation/getting_started/content/04-tutorials/_jupyter_dashboard_1.ipynb +2 -2
- solara/website/pages/documentation/getting_started/content/05-fundamentals/10-components.md +19 -14
- solara/website/pages/documentation/getting_started/content/05-fundamentals/50-state-management.md +205 -15
- solara/website/pages/documentation/getting_started/content/07-deploying/10-self-hosted.md +3 -1
- solara/website/pages/home.vue +1199 -0
- solara/website/pages/our_team/__init__.py +83 -0
- solara/website/pages/pricing/__init__.py +31 -0
- solara/website/pages/roadmap/__init__.py +11 -0
- solara/website/pages/roadmap/roadmap.md +41 -0
- solara/website/pages/scale_ipywidgets.py +45 -0
- solara/widgets/vue/gridlayout.vue +1 -1
- solara/widgets/widgets.py +8 -0
- {solara_ui-1.38.0.dist-info → solara_ui-1.40.0.dist-info}/METADATA +2 -2
- {solara_ui-1.38.0.dist-info → solara_ui-1.40.0.dist-info}/RECORD +75 -59
- solara/website/components/hero.py +0 -15
- solara/website/pages/contact/contact.md +0 -17
- {solara_ui-1.38.0.data → solara_ui-1.40.0.data}/data/etc/jupyter/jupyter_notebook_config.d/solara.json +0 -0
- {solara_ui-1.38.0.data → solara_ui-1.40.0.data}/data/etc/jupyter/jupyter_server_config.d/solara.json +0 -0
- {solara_ui-1.38.0.dist-info → solara_ui-1.40.0.dist-info}/WHEEL +0 -0
- {solara_ui-1.38.0.dist-info → solara_ui-1.40.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
|
|
32
32
|
<script id="jupyter-config-data" type="application/json">
|
|
33
33
|
{
|
|
34
|
-
"baseUrl": "{{
|
|
34
|
+
"baseUrl": "{{jupyter_root_path}}/",
|
|
35
35
|
"kernelId": "1234"
|
|
36
36
|
}
|
|
37
37
|
</script>
|
|
@@ -243,12 +243,13 @@
|
|
|
243
243
|
{% endif %}
|
|
244
244
|
<script>
|
|
245
245
|
solara.rootPath = {{ root_path | tojson | safe}};
|
|
246
|
+
solara.jupyterRootPath = {{ jupyter_root_path | tojson | safe}};
|
|
246
247
|
solara.cdn = {{ cdn | tojson | safe }};
|
|
247
248
|
// the vue templates expect it to not have a trailing slash
|
|
248
249
|
solara.cdn = solara.cdn.replace(/\/$/, '');
|
|
249
250
|
// keep this for backwards compatibility
|
|
250
251
|
window.solara_cdn = solara.cdn;
|
|
251
|
-
console.log("
|
|
252
|
+
console.log("solara config", {rootPath: solara.rootPath, jupyterRootPath: solara.jupyterRootPath, cdn: solara.cdn});
|
|
252
253
|
|
|
253
254
|
async function changeThemeCSS(theme) {
|
|
254
255
|
let css = await fetch(`${solara.rootPath}/static/assets/theme-${theme}.css`).then(r => r.text());
|
|
@@ -269,6 +270,12 @@
|
|
|
269
270
|
solara.production = {{ production | tojson | safe }};
|
|
270
271
|
const themeVariants = ['light', 'dark', 'auto']
|
|
271
272
|
solara.preRendered = {{ pre_rendered_html | safe | length | tojson }} > 0
|
|
273
|
+
if(solara.preRendered) {
|
|
274
|
+
document.body.classList.add('solara-ssg', 'solara-pre-rendered');
|
|
275
|
+
} else {
|
|
276
|
+
document.body.classList.add('solara-no-ssg');
|
|
277
|
+
}
|
|
278
|
+
|
|
272
279
|
</script>
|
|
273
280
|
|
|
274
281
|
<script>
|
|
@@ -329,8 +336,25 @@
|
|
|
329
336
|
// if preRendered, the app is not mounted yet
|
|
330
337
|
// so we mount it when loading becomes false
|
|
331
338
|
if (solara.preRendered && !this.mounted) {
|
|
332
|
-
|
|
333
|
-
|
|
339
|
+
function waitForAnimation(element) {
|
|
340
|
+
return new Promise((resolve) => {
|
|
341
|
+
element.addEventListener('animationend', resolve, { once: true });
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function waitForAllAnimations(elements) {
|
|
346
|
+
const animationPromises = Array.from(elements).map(element => waitForAnimation(element));
|
|
347
|
+
return Promise.all(animationPromises);
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
(async () => {
|
|
351
|
+
await waitForAllAnimations(document.querySelectorAll('.solara-ssg-wait-for-animation'));
|
|
352
|
+
console.log("animation finished");
|
|
353
|
+
document.body.classList.remove('solara-pre-rendered');
|
|
354
|
+
document.body.classList.add('solara-pre-rendered-finished');
|
|
355
|
+
this.isMounted = true;
|
|
356
|
+
this.$mount("#app")
|
|
357
|
+
})();
|
|
334
358
|
} else {
|
|
335
359
|
this.isMounted = true;
|
|
336
360
|
}
|
|
@@ -418,7 +442,7 @@
|
|
|
418
442
|
{% endif -%}
|
|
419
443
|
nbextensionHashes = {{ resources.nbextensions_hashes | tojson | safe }};
|
|
420
444
|
requirejs.config({
|
|
421
|
-
baseUrl: '{{
|
|
445
|
+
baseUrl: '{{jupyter_root_path}}',
|
|
422
446
|
waitSeconds: 3000,
|
|
423
447
|
map: {
|
|
424
448
|
'*': {
|
|
@@ -443,7 +467,7 @@
|
|
|
443
467
|
});
|
|
444
468
|
requirejs([
|
|
445
469
|
{% for ext in resources.nbextensions if ext != 'jupyter-vuetify/extension' and ext != 'jupyter-vue/extension' -%}
|
|
446
|
-
"{{
|
|
470
|
+
"{{jupyter_root_path}}/nbextensions/{{ ext }}.js",
|
|
447
471
|
{% endfor %}
|
|
448
472
|
]);
|
|
449
473
|
(async function () {
|
solara/website/assets/custom.css
CHANGED
|
@@ -34,6 +34,17 @@
|
|
|
34
34
|
--docs-social-twitter: #000000;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
.theme--dark {
|
|
38
|
+
--color-border-appbar: hsla(0,0%,100%,.12);
|
|
39
|
+
--color-material-background: #1e1e1e;
|
|
40
|
+
--color-text-fade: hsla(0,0%,100%,.7);
|
|
41
|
+
}
|
|
42
|
+
.theme--light {
|
|
43
|
+
--color-border-appbar: rgba(0,0,0,.12);
|
|
44
|
+
--color-material-background: #fff;
|
|
45
|
+
--color-text-fade: rgba(0,0,0,.6);
|
|
46
|
+
}
|
|
47
|
+
|
|
37
48
|
html,
|
|
38
49
|
body,
|
|
39
50
|
p,
|
|
@@ -199,9 +210,8 @@ blockquote p:last-child {
|
|
|
199
210
|
}
|
|
200
211
|
.v-sheet.docs-card-container{
|
|
201
212
|
width: 100%;
|
|
202
|
-
padding: 0 10%;
|
|
203
213
|
justify-content: start;
|
|
204
|
-
align-content:
|
|
214
|
+
align-content: stretch;
|
|
205
215
|
flex-wrap: wrap !important;
|
|
206
216
|
flex-grow: 1;
|
|
207
217
|
row-gap: 20px;
|
|
@@ -211,11 +221,14 @@ blockquote p:last-child {
|
|
|
211
221
|
}
|
|
212
222
|
.v-sheet.docs-card-container > a > .docs-card{
|
|
213
223
|
display: flex;
|
|
214
|
-
|
|
215
|
-
height:
|
|
224
|
+
flex-grow: 1;
|
|
225
|
+
height: 4rem;
|
|
216
226
|
border-radius: 16px !important;
|
|
217
227
|
color: var(--color-grey-light);
|
|
218
228
|
}
|
|
229
|
+
.v-sheet.docs-card-container > a > .docs-card > h2 {
|
|
230
|
+
padding: 0 24px;
|
|
231
|
+
}
|
|
219
232
|
.v-sheet.docs-card-container > a .docs-card-icon{
|
|
220
233
|
padding-right: 1.5rem;
|
|
221
234
|
transition: ease padding 0.25s;
|
|
@@ -274,6 +287,8 @@ blockquote p:last-child {
|
|
|
274
287
|
/* header */
|
|
275
288
|
.header-logo {
|
|
276
289
|
display: block;
|
|
290
|
+
height: 100%;
|
|
291
|
+
margin: 0;
|
|
277
292
|
}
|
|
278
293
|
.news {
|
|
279
294
|
background-color: var(--color-primary);
|
|
@@ -289,46 +304,6 @@ blockquote p:last-child {
|
|
|
289
304
|
color: white !important;
|
|
290
305
|
}
|
|
291
306
|
|
|
292
|
-
/* hero */
|
|
293
|
-
.hero {
|
|
294
|
-
background: var(--color-primary-lightest) url(https://dxhl76zpt6fap.cloudfront.net/public/hero.webp);
|
|
295
|
-
background-size: cover;
|
|
296
|
-
min-height: 660px;
|
|
297
|
-
padding: 7rem 0;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
.theme--dark .hero {
|
|
301
|
-
background: var(--dark-color-primary-lightest) url(https://dxhl76zpt6fap.cloudfront.net/public/hero.webp);
|
|
302
|
-
background-size: cover;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
.hero h1 {
|
|
306
|
-
font-size: 4.5rem;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
.hero h2 {
|
|
310
|
-
padding-left: 150px;
|
|
311
|
-
padding-right: 150px;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
.hero b {
|
|
315
|
-
color: var(--color-primary);
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
.theme--dark .hero b {
|
|
319
|
-
color: var(--dark-color-primary);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
@media screen and (max-width: 960px) {
|
|
323
|
-
.hero {
|
|
324
|
-
padding: 4rem 0;
|
|
325
|
-
min-height: auto;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
.hero h1 {
|
|
329
|
-
font-size: 3rem;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
307
|
|
|
333
308
|
/* fixes padding issue on drawer menu */
|
|
334
309
|
.v-list-group__items {
|
|
@@ -362,7 +337,7 @@ blockquote p:last-child {
|
|
|
362
337
|
|
|
363
338
|
.menu li a:link {
|
|
364
339
|
display: block;
|
|
365
|
-
padding:
|
|
340
|
+
padding: 0.75rem;
|
|
366
341
|
}
|
|
367
342
|
|
|
368
343
|
.menu .active {
|
|
@@ -427,11 +402,6 @@ blockquote p:last-child {
|
|
|
427
402
|
font-weight: bolder;
|
|
428
403
|
}
|
|
429
404
|
|
|
430
|
-
.solara-autorouter-content {
|
|
431
|
-
padding-top: 2rem;
|
|
432
|
-
max-width: 100%;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
405
|
.jp-RenderedHTMLCommon {
|
|
436
406
|
padding-right: 0;
|
|
437
407
|
}
|
|
@@ -457,13 +427,6 @@ blockquote p:last-child {
|
|
|
457
427
|
/* MOBILE */
|
|
458
428
|
|
|
459
429
|
@media screen and (aspect-ratio < 1) {
|
|
460
|
-
.hero{
|
|
461
|
-
background-position-x: 29%;
|
|
462
|
-
}
|
|
463
|
-
.hero h2{
|
|
464
|
-
padding-left: 0;
|
|
465
|
-
padding-right: 0;
|
|
466
|
-
}
|
|
467
430
|
.container.fill-height > .ma-8.row{
|
|
468
431
|
max-width: 80%;
|
|
469
432
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from .docs import CategoryLayout, Gallery, NoPage, SubCategoryLayout, WithCode # noqa
|
|
2
2
|
from .header import Header
|
|
3
|
-
from .
|
|
3
|
+
from .markdown import MarkdownWithMetadata
|
|
4
4
|
|
|
5
|
-
__all__ = ["Header", "
|
|
5
|
+
__all__ = ["Header", "MarkdownWithMetadata", "CategoryLayout", "Gallery", "NoPage", "SubCategoryLayout", "WithCode"]
|
|
@@ -7,14 +7,19 @@
|
|
|
7
7
|
v-model="query"
|
|
8
8
|
prepend-inner-icon="mdi-magnify"
|
|
9
9
|
hide-details
|
|
10
|
+
dense
|
|
10
11
|
:placeholder="mac ? '⌘K to search' : 'Ctrl+K to search'"
|
|
12
|
+
filled
|
|
11
13
|
outlined
|
|
12
|
-
rounded
|
|
13
14
|
clearable
|
|
14
15
|
ref="search"
|
|
15
16
|
style="flex-grow: 1; max-width: 650px;"
|
|
16
17
|
@click="show($event, on);"
|
|
17
|
-
@keyup.enter="item = 0"
|
|
18
|
+
@keyup.enter="hoverItem === null ? item = 0 : item = hoverItem"
|
|
19
|
+
@keyup.esc="close();"
|
|
20
|
+
@keyup.down="hoverItem = hoverItem == null ? 0 : Math.min(hoverItem + 1, results.hits.length - 1, 9)"
|
|
21
|
+
@keyup.up="hoverItem = hoverItem == null ? 9 : Math.max(hoverItem - 1, 0)"
|
|
22
|
+
@focus="selectText();"
|
|
18
23
|
class="algolia"
|
|
19
24
|
></v-text-field>
|
|
20
25
|
</template>
|
|
@@ -27,7 +32,7 @@
|
|
|
27
32
|
</v-list>
|
|
28
33
|
<v-list v-else :style="{width: menuWidth + 'px'}">
|
|
29
34
|
<v-list-item-group v-model="item">
|
|
30
|
-
<v-list-item v-for="(element, index) in this.results.hits" :key="element['url']">
|
|
35
|
+
<v-list-item v-for="(element, index) in this.results.hits" :key="element['url']" :input-value="index === hoverItem">
|
|
31
36
|
<v-list-item-content>
|
|
32
37
|
<v-list-item-title>
|
|
33
38
|
{{ element.hierarchy.lvl1 }}
|
|
@@ -39,7 +44,7 @@
|
|
|
39
44
|
</v-list-item-content>
|
|
40
45
|
</v-list-item>
|
|
41
46
|
</v-list-item-group>
|
|
42
|
-
<v-list-item>
|
|
47
|
+
<v-list-item v-if="this.results.nbHits > 10">
|
|
43
48
|
<v-list-item-content>
|
|
44
49
|
<v-list-item-title>And {{ this.results.nbHits - 10}} More...</v-list-item-title>
|
|
45
50
|
</v-list-item-content>
|
|
@@ -58,7 +63,6 @@ module.exports = {
|
|
|
58
63
|
window.search = this;
|
|
59
64
|
this.updateMenuWidth();
|
|
60
65
|
window.addEventListener('resize', this.updateMenuWidth);
|
|
61
|
-
|
|
62
66
|
},
|
|
63
67
|
watch: {
|
|
64
68
|
query ( value ) {
|
|
@@ -66,12 +70,17 @@ module.exports = {
|
|
|
66
70
|
this.search();
|
|
67
71
|
},
|
|
68
72
|
item ( value ) {
|
|
69
|
-
if (
|
|
73
|
+
if ( value === null ) return;
|
|
74
|
+
if ( this.results.hits != null && this.results.hits.length >= value ) {
|
|
70
75
|
let url = this.results.hits[value].url;
|
|
71
76
|
if (url.startsWith("https://solara.dev")) {
|
|
72
77
|
url = url.slice(18);
|
|
73
78
|
}
|
|
74
79
|
solara.router.push( url );
|
|
80
|
+
this.close();
|
|
81
|
+
// reset the search
|
|
82
|
+
this.item = null;
|
|
83
|
+
this.hoverItem = null;
|
|
75
84
|
}
|
|
76
85
|
},
|
|
77
86
|
},
|
|
@@ -82,6 +91,10 @@ module.exports = {
|
|
|
82
91
|
on.click( e )
|
|
83
92
|
} );
|
|
84
93
|
},
|
|
94
|
+
close() {
|
|
95
|
+
this.show_results = false;
|
|
96
|
+
this.$refs.search.blur();
|
|
97
|
+
},
|
|
85
98
|
initSearch() {
|
|
86
99
|
this.client = this.algoliasearch( '9KW9L7O5EQ', '647ca12ba642437cc40c2adee4a78d08' );
|
|
87
100
|
this.index = this.client.initIndex( 'solara' );
|
|
@@ -107,6 +120,9 @@ module.exports = {
|
|
|
107
120
|
this.$set(this.results, []);
|
|
108
121
|
}
|
|
109
122
|
},
|
|
123
|
+
selectText() {
|
|
124
|
+
this.$refs.search.$refs.input.select();
|
|
125
|
+
},
|
|
110
126
|
getSnippet( element ) {
|
|
111
127
|
if (element.type == "content") {
|
|
112
128
|
return element._highlightResult.content.value;
|
|
@@ -176,6 +192,7 @@ module.exports = {
|
|
|
176
192
|
query: '',
|
|
177
193
|
results: [],
|
|
178
194
|
item: null,
|
|
195
|
+
hoverItem: null,
|
|
179
196
|
show_results: false,
|
|
180
197
|
mac: false,
|
|
181
198
|
menuWidth: 0,
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
import solara
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@solara.component
|
|
6
|
+
def BreadCrumbs():
|
|
7
|
+
router = solara.use_router()
|
|
8
|
+
routes = router.path_routes
|
|
9
|
+
|
|
10
|
+
with solara.Row(style={"align-items": "center", "flex-wrap": "wrap"}) as main:
|
|
11
|
+
for i, route in enumerate(routes):
|
|
12
|
+
if i == len(routes) - 1:
|
|
13
|
+
solara.Text(route.label or route.path, style={"color": "var(--color-text-fade)"})
|
|
14
|
+
else:
|
|
15
|
+
with solara.Link(solara.resolve_path(route), style={"color": "var(--color-text-fade)"}):
|
|
16
|
+
solara.Text(route.label or route.path)
|
|
17
|
+
if i != len(routes) - 1:
|
|
18
|
+
solara.Text("/", style={"font-size": "1.5rem", "color": "var(--color-text-fade)"})
|
|
19
|
+
return main
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _resolve_path_to_route(path_to_find: List[str], all_routes: List[solara.Route], routes: List[solara.Route] = []):
|
|
23
|
+
if len(path_to_find) == 0:
|
|
24
|
+
return routes
|
|
25
|
+
for route in all_routes:
|
|
26
|
+
if path_to_find[0] == route.path:
|
|
27
|
+
routes += [route]
|
|
28
|
+
return _resolve_path_to_route(path_to_find[1:], route.children, routes)
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Optional
|
|
6
|
+
import solara
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
postmark_api_key = None
|
|
10
|
+
contact_email_address = None
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
postmark_api_key = os.environ["POSTMARK_API_KEY"]
|
|
14
|
+
contact_email_address = os.environ["SOLARA_CONTACT_EMAIL_ADDRESS"]
|
|
15
|
+
except Exception:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@solara.component
|
|
20
|
+
def Contact(
|
|
21
|
+
style: Dict[str, Any] = {},
|
|
22
|
+
title="Contact Us",
|
|
23
|
+
subtitle="We'd love to hear from you!",
|
|
24
|
+
submit_label="Submit",
|
|
25
|
+
email_subject="Contact Form Submission",
|
|
26
|
+
):
|
|
27
|
+
first_name = solara.use_reactive("")
|
|
28
|
+
last_name = solara.use_reactive("")
|
|
29
|
+
email = solara.use_reactive("")
|
|
30
|
+
company = solara.use_reactive("")
|
|
31
|
+
message = solara.use_reactive("")
|
|
32
|
+
error: solara.Reactive[Optional[str]] = solara.use_reactive(None)
|
|
33
|
+
success = solara.use_reactive(False)
|
|
34
|
+
|
|
35
|
+
def send(*_ignore):
|
|
36
|
+
if postmark_api_key is None or contact_email_address is None:
|
|
37
|
+
error.set("Email service not properly configured. Please contact the site administrator at solara@widgetti.io.")
|
|
38
|
+
elif not first_name.value or not last_name.value or not email.value or not message.value:
|
|
39
|
+
error.set("Please fill out all required fields.")
|
|
40
|
+
else:
|
|
41
|
+
# Create the email content
|
|
42
|
+
msg = {}
|
|
43
|
+
msg["From"] = contact_email_address
|
|
44
|
+
msg["To"] = contact_email_address
|
|
45
|
+
msg["Subject"] = email_subject
|
|
46
|
+
msg["ReplyTo"] = email.value
|
|
47
|
+
|
|
48
|
+
# Email body
|
|
49
|
+
msg["HtmlBody"] = f"""
|
|
50
|
+
<b>First Name</b>: {first_name.value}<br />
|
|
51
|
+
<b>Last Name</b>: {last_name.value}<br />
|
|
52
|
+
<b>Email</b>: {email.value}<br />
|
|
53
|
+
<b>Company</b>: {company.value}<br />
|
|
54
|
+
<b>Message</b>: {message.value}<br />
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
# Send emails
|
|
58
|
+
try:
|
|
59
|
+
requests.post(
|
|
60
|
+
"https://api.postmarkapp.com/email",
|
|
61
|
+
headers={
|
|
62
|
+
"Accept": "application/json",
|
|
63
|
+
"Content-Type": "application/json",
|
|
64
|
+
"X-Postmark-Server-Token": postmark_api_key,
|
|
65
|
+
},
|
|
66
|
+
data=json.dumps(msg),
|
|
67
|
+
)
|
|
68
|
+
requests.post(
|
|
69
|
+
"https://api.postmarkapp.com/email",
|
|
70
|
+
headers={
|
|
71
|
+
"Accept": "application/json",
|
|
72
|
+
"Content-Type": "application/json",
|
|
73
|
+
"X-Postmark-Server-Token": postmark_api_key,
|
|
74
|
+
},
|
|
75
|
+
data=json.dumps(
|
|
76
|
+
{
|
|
77
|
+
"From": contact_email_address,
|
|
78
|
+
"To": email.value,
|
|
79
|
+
"Subject": "Thank you for contacting Solara",
|
|
80
|
+
"HtmlBody": f"""
|
|
81
|
+
<p>Hi {first_name.value},</p>
|
|
82
|
+
<p>Thank you for contacting us! We will get back to you as soon as possible.</p>
|
|
83
|
+
<p>Best regards,<br />The Solara Team</p>
|
|
84
|
+
""",
|
|
85
|
+
}
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
except Exception as e:
|
|
89
|
+
error.set(f"Error sending email: {e}")
|
|
90
|
+
else:
|
|
91
|
+
success.set(True)
|
|
92
|
+
error.set(None)
|
|
93
|
+
first_name.set("")
|
|
94
|
+
last_name.set("")
|
|
95
|
+
email.set("")
|
|
96
|
+
company.set("")
|
|
97
|
+
message.set("")
|
|
98
|
+
|
|
99
|
+
with solara.Card(title=title, style={"width": "100%", "max-width": "1024px", **style}):
|
|
100
|
+
solara.Markdown(subtitle)
|
|
101
|
+
solara.Text("* Required fields")
|
|
102
|
+
with solara.Row():
|
|
103
|
+
solara.InputText(label="First Name *", value=first_name)
|
|
104
|
+
solara.InputText(label="Last Name *", value=last_name)
|
|
105
|
+
with solara.Row():
|
|
106
|
+
solara.InputText(label="Email *", value=email)
|
|
107
|
+
solara.InputText(label="Company", value=company)
|
|
108
|
+
solara.v.Textarea(placeholder="Message *", v_model=message.value, on_v_model=message.set)
|
|
109
|
+
with solara.CardActions():
|
|
110
|
+
solara.Button(label=submit_label, color="primary", on_click=send)
|
|
111
|
+
solara.Button(
|
|
112
|
+
label="Clear",
|
|
113
|
+
color="secondary",
|
|
114
|
+
text=True,
|
|
115
|
+
on_click=lambda: [first_name.set(""), last_name.set(""), email.set(""), company.set(""), message.set("")],
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
solara.Style(
|
|
119
|
+
"""
|
|
120
|
+
.v-snack__wrapper {
|
|
121
|
+
box-shadow: none;
|
|
122
|
+
}
|
|
123
|
+
"""
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
with solara.v.Snackbar(
|
|
127
|
+
v_model=error.value is not None,
|
|
128
|
+
timeout=5000,
|
|
129
|
+
on_v_model=lambda *_: error.set(None),
|
|
130
|
+
left=True,
|
|
131
|
+
color="error",
|
|
132
|
+
):
|
|
133
|
+
solara.Markdown(error.value or "", style={"--dark-color-text": "white", "--color-text": "white"})
|
|
134
|
+
solara.Button(icon=True, icon_name="mdi-close", color="white", on_click=lambda: error.set(None))
|
|
135
|
+
|
|
136
|
+
with solara.v.Snackbar(
|
|
137
|
+
v_model=success.value,
|
|
138
|
+
timeout=5000,
|
|
139
|
+
on_v_model=lambda *_: success.set(False),
|
|
140
|
+
left=True,
|
|
141
|
+
color="success",
|
|
142
|
+
):
|
|
143
|
+
solara.Markdown("Your message has been sent!", style={"--dark-color-text": "white", "--color-text": "white"})
|
|
144
|
+
solara.Button(icon=True, icon_name="mdi-close", color="white", on_click=lambda: success.set(False))
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import solara
|
|
2
|
-
from
|
|
2
|
+
from .markdown import MarkdownWithMetadata
|
|
3
|
+
from .breadcrumbs import BreadCrumbs
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
@solara.component
|
|
@@ -66,18 +67,19 @@ def NoPage():
|
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
@solara.component
|
|
69
|
-
def WithCode(
|
|
70
|
-
component = getattr(module, "Page", None)
|
|
71
|
-
with
|
|
70
|
+
def WithCode(route_current):
|
|
71
|
+
component = getattr(route_current.module, "Page", None)
|
|
72
|
+
with solara.Column(style={"flex-grow": 1, "padding-top": "56px"}) as main:
|
|
73
|
+
BreadCrumbs()
|
|
72
74
|
# It renders code better
|
|
73
|
-
|
|
74
|
-
module.__doc__ or "# no docs yet",
|
|
75
|
+
MarkdownWithMetadata(
|
|
76
|
+
route_current.module.__doc__ or "# no docs yet",
|
|
75
77
|
unsafe_solara_execute=True,
|
|
76
78
|
)
|
|
77
79
|
if component and component != NoPage:
|
|
78
80
|
with solara.Card("Example", margin=0, classes=["mt-8"]):
|
|
79
81
|
component()
|
|
80
|
-
github_url = solara.util.github_url(module.__file__)
|
|
82
|
+
github_url = solara.util.github_url(route_current.module.__file__)
|
|
81
83
|
solara.Button(
|
|
82
84
|
label="View source",
|
|
83
85
|
icon_name="mdi-github-circle",
|
|
@@ -97,7 +99,7 @@ def SubCategoryLayout(children=[]):
|
|
|
97
99
|
elif route_current.path == "/":
|
|
98
100
|
return solara.Error("Not supposed to be rendered")
|
|
99
101
|
elif route_current.module:
|
|
100
|
-
WithCode(route_current
|
|
102
|
+
WithCode(route_current)
|
|
101
103
|
else:
|
|
102
104
|
with solara.Column(align="center", children=children, style={"flex-grow": 1, "padding": "0"}) as main:
|
|
103
105
|
pass
|
|
@@ -113,6 +115,6 @@ def CategoryLayout(children=[]):
|
|
|
113
115
|
if route_current.path == "/":
|
|
114
116
|
return Gallery()
|
|
115
117
|
else:
|
|
116
|
-
with solara.Column(align="stretch", style={"width": "
|
|
118
|
+
with solara.Column(align="stretch", children=children, style={"max-width": "100%"}) as main:
|
|
117
119
|
pass
|
|
118
120
|
return main
|
|
@@ -20,34 +20,44 @@ def Header(
|
|
|
20
20
|
|
|
21
21
|
# set states for menu
|
|
22
22
|
with solara.Column(gap="0px"):
|
|
23
|
-
with solara.Div(classes=["news"]):
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
with solara.v.AppBar(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
23
|
+
# with solara.Div(classes=["news"]):
|
|
24
|
+
# solara.HTML(
|
|
25
|
+
# "div",
|
|
26
|
+
# unsafe_innerHTML="<a href='https://github.com/widgetti/solara' target='_blank' >Star us on github 🤩</a>",
|
|
27
|
+
# )
|
|
28
|
+
with solara.v.AppBar(
|
|
29
|
+
tag="header", flat=True, clipped_left=True, style_="background-color: transparent; border-bottom: 1px solid var(--color-border-appbar);"
|
|
30
|
+
):
|
|
31
|
+
if route_current is not None and route_current.module is not None and hasattr(route_current.module, "Sidebar"):
|
|
32
|
+
with solara.Button(icon=True, class_="hidden-md-and-up", on_click=lambda: on_toggle_left_menu and on_toggle_left_menu()):
|
|
33
|
+
rv.Icon(children=["mdi-menu"])
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
display = " d-none d-sm-flex" if route_current is not None and route_current.path not in ["about", "pricing", "careers"] else " d-flex"
|
|
36
|
+
with solara.v.Html(
|
|
37
|
+
tag="div",
|
|
38
|
+
class_="header-logo-container" + display,
|
|
39
|
+
style_="""
|
|
40
|
+
background-color: transparent;
|
|
41
|
+
flex-grow: 1;
|
|
42
|
+
align-items: stretch;
|
|
43
|
+
max-height: 65%;
|
|
44
|
+
""",
|
|
36
45
|
):
|
|
37
|
-
with solara.Link(path_or_route="/"):
|
|
46
|
+
with solara.Link(path_or_route="/", style={"display": "flex", "align-items": "center", "flex-direction": "row", "gap": "10px"}):
|
|
38
47
|
solara.Image(router.root_path + f"/static/assets/images/logo{'_white' if dark_effective else ''}.svg", classes=["header-logo"])
|
|
48
|
+
solara.Text("API", style={"font-size": "20px", "font-weight": "600"})
|
|
39
49
|
|
|
40
|
-
|
|
50
|
+
if route_current is not None and route_current.path not in ["about", "pricing", "careers"]:
|
|
41
51
|
if settings.search.enabled:
|
|
42
52
|
from solara_enterprise.search.search import Search
|
|
43
53
|
|
|
44
54
|
Search()
|
|
45
55
|
else:
|
|
46
|
-
|
|
47
|
-
Algolia()
|
|
56
|
+
Algolia()
|
|
48
57
|
|
|
58
|
+
with rv.Html(tag="ul", class_="main-menu menu d-none d-md-flex", style_="justify-content: flex-end;"):
|
|
49
59
|
for route in all_routes:
|
|
50
|
-
if route.path in ["apps", "contact", "changelog"]:
|
|
60
|
+
if route.path in ["apps", "contact", "changelog", "our_team", "about", "pricing", "roadmap", "careers"]:
|
|
51
61
|
continue
|
|
52
62
|
current = route_current == route
|
|
53
63
|
with rv.Html(tag="li", class_="active" if current else None):
|
|
@@ -58,7 +68,8 @@ def Header(
|
|
|
58
68
|
with rv.Btn(icon=True, tag="a", class_="d-none d-md-flex", attributes={"href": "https://discord.solara.dev", "target": "_blank"}):
|
|
59
69
|
rv.Icon(children=["mdi-discord"])
|
|
60
70
|
|
|
61
|
-
solara.
|
|
71
|
+
with solara.v.Html(tag="div", class_="d-none d-md-flex"):
|
|
72
|
+
solara.lab.ThemeToggle()
|
|
62
73
|
|
|
63
|
-
with solara.Button(icon=True, class_="hidden-md-and-up", on_click=lambda: on_toggle_right_menu and on_toggle_right_menu()):
|
|
64
|
-
|
|
74
|
+
# with solara.Button(icon=True, class_="hidden-md-and-up", on_click=lambda: on_toggle_right_menu and on_toggle_right_menu()):
|
|
75
|
+
# rv.Icon(children=["mdi-menu"])
|
|
@@ -27,4 +27,15 @@ def MarkdownWithMetadata(content: str, unsafe_solara_execute=True):
|
|
|
27
27
|
solara.Meta(property=key, content=value)
|
|
28
28
|
else:
|
|
29
29
|
solara.Meta(name=key, content=value)
|
|
30
|
-
solara.
|
|
30
|
+
with solara.v.Html(
|
|
31
|
+
tag="div",
|
|
32
|
+
style_="display: flex; flex-direction: row; justify-content: center; gap: 15px; max-width: 90%; margin: 0 auto;",
|
|
33
|
+
attributes={"id": "markdown-to-navigate"},
|
|
34
|
+
):
|
|
35
|
+
solara.Markdown(content, unsafe_solara_execute=unsafe_solara_execute, style="flex-grow: 1; max-width: min(100%, 1024px);")
|
|
36
|
+
MarkdownNavigation(id="markdown-to-navigate").key("markdown-nav" + str(hash(content)))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@solara.component_vue("markdown_nav.vue")
|
|
40
|
+
def MarkdownNavigation(id: str):
|
|
41
|
+
pass
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-list dense :style="`margin: 40px 24px 0 24px; position: sticky; top: 88px; align-self: flex-start;`" class="d-none d-lg-inline-block">
|
|
3
|
+
<v-list-item
|
|
4
|
+
v-for="(element, index) in this.sections"
|
|
5
|
+
:key="index"
|
|
6
|
+
:href="'#' + element.id"
|
|
7
|
+
:style="'padding-left: ' + (16 + (element.nodeName[1] - 1) * 20) + 'px;'"
|
|
8
|
+
>
|
|
9
|
+
<v-list-item-content>
|
|
10
|
+
<v-list-item-title>
|
|
11
|
+
{{ element.innerText }}
|
|
12
|
+
</v-list-item-title>
|
|
13
|
+
</v-list-item-content>
|
|
14
|
+
</v-list-item>
|
|
15
|
+
</v-list>
|
|
16
|
+
</template>
|
|
17
|
+
<script>
|
|
18
|
+
module.exports = {
|
|
19
|
+
mounted() {
|
|
20
|
+
this.getSections();
|
|
21
|
+
},
|
|
22
|
+
methods: {
|
|
23
|
+
getSections() {
|
|
24
|
+
const parentToSearch = document.getElementById(this.id);
|
|
25
|
+
this.sections = parentToSearch.querySelectorAll('h1, h2, h3, h4');
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
data() {
|
|
29
|
+
return {
|
|
30
|
+
sections: []
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
</script>
|