Skip to content

Commit 1ae83e3

Browse files
committed
Make a new view to show the LinkedFile code. Add Prism. Provide buttons to switch between code and demo.
1 parent 3bf7773 commit 1ae83e3

File tree

11 files changed

+249
-13
lines changed

11 files changed

+249
-13
lines changed

src/psc/app.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,25 @@ async def example(request: Request) -> _TemplateResponse:
132132
)
133133

134134

135+
async def example_code(request: Request) -> _TemplateResponse:
136+
"""Handle the linked files for the code example."""
137+
example_name = request.path_params["example_name"]
138+
resources: Resources = request.app.state.resources
139+
this_example = resources.examples[example_name]
140+
root_path = "../../.."
141+
142+
return templates.TemplateResponse(
143+
"example_code.jinja2",
144+
dict(
145+
title=f"{this_example.title} Code",
146+
extra_head=this_example.extra_head,
147+
request=request,
148+
root_path=root_path,
149+
linked_files=this_example.linked_files,
150+
),
151+
)
152+
153+
135154
async def content_page(request: Request) -> _TemplateResponse:
136155
"""Handle a content page."""
137156
page_name = request.path_params["page_name"]
@@ -159,6 +178,7 @@ async def content_page(request: Request) -> _TemplateResponse:
159178
Route("/authors", authors),
160179
Route("/authors/{author_name}.html", author),
161180
Route("/gallery/examples/{example_name}/index.html", example),
181+
Route("/gallery/examples/{example_name}/code.html", example_code),
162182
Route("/gallery/examples/{example_name}/", example),
163183
Route("/pages/{page_name}.html", content_page),
164184
Mount("/gallery", StaticFiles(directory=HERE / "gallery")),
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
h1 {
2+
font-weight: normal;
23
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
---
22
title: Hello World
33
subtitle: The classic hello world, but in Python -- in a browser!
4+
linked_files:
5+
- hello_world.css
6+
- hello_world.js
47
---
58
The *body* description.

src/psc/gallery/examples/interest_calculator/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Compound Interest Calculator
33
subtitle: Enter some numbers, get some numbers.
44
author: meg-1
5-
linked_files:
5+
linked_files:
66
- calculator.py
77
- styles.css
88
---

src/psc/resources.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@
1717
from psc.here import HERE
1818
from psc.here import PYODIDE
1919

20+
2021
EXCLUSIONS = ("pyscript.css", "pyscript.js", "favicon.png")
2122

2223

2324
def tag_filter(
24-
tag: Tag,
25-
exclusions: tuple[str, ...] = EXCLUSIONS,
25+
tag: Tag,
26+
exclusions: tuple[str, ...] = EXCLUSIONS,
2627
) -> bool:
2728
"""Filter nodes from example that should not get included."""
2829
attr = "href" if tag.name == "link" else "src"
@@ -83,7 +84,8 @@ class Resource:
8384
linked_file_mapping = dict(
8485
py="python",
8586
css="css",
86-
html="html"
87+
html="html",
88+
js="javascript",
8789
)
8890

8991

src/psc/static/prism.css

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/* PrismJS 1.29.0
2+
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+python&plugins=custom-class+toolbar+copy-to-clipboard+download-button */
3+
code[class*=language-], pre[class*=language-] {
4+
color: #000;
5+
background: 0 0;
6+
text-shadow: 0 1px #fff;
7+
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
8+
font-size: 1em;
9+
text-align: left;
10+
white-space: pre;
11+
word-spacing: normal;
12+
word-break: normal;
13+
word-wrap: normal;
14+
line-height: 1.5;
15+
-moz-tab-size: 4;
16+
-o-tab-size: 4;
17+
tab-size: 4;
18+
-webkit-hyphens: none;
19+
-moz-hyphens: none;
20+
-ms-hyphens: none;
21+
hyphens: none
22+
}
23+
24+
code[class*=language-] ::-moz-selection, code[class*=language-]::-moz-selection, pre[class*=language-] ::-moz-selection, pre[class*=language-]::-moz-selection {
25+
text-shadow: none;
26+
background: #b3d4fc
27+
}
28+
29+
code[class*=language-] ::selection, code[class*=language-]::selection, pre[class*=language-] ::selection, pre[class*=language-]::selection {
30+
text-shadow: none;
31+
background: #b3d4fc
32+
}
33+
34+
@media print {
35+
code[class*=language-], pre[class*=language-] {
36+
text-shadow: none
37+
}
38+
}
39+
40+
pre[class*=language-] {
41+
padding: 1em;
42+
margin: .5em 0;
43+
overflow: auto
44+
}
45+
46+
:not(pre) > code[class*=language-], pre[class*=language-] {
47+
background: #f5f2f0
48+
}
49+
50+
:not(pre) > code[class*=language-] {
51+
padding: .1em;
52+
border-radius: .3em;
53+
white-space: normal
54+
}
55+
56+
.prism--token.prism--cdata, .prism--token.prism--comment, .prism--token.prism--doctype, .prism--token.prism--prolog {
57+
color: #708090
58+
}
59+
60+
.prism--token.prism--punctuation {
61+
color: #999
62+
}
63+
64+
.prism--token.prism--namespace {
65+
opacity: .7
66+
}
67+
68+
.prism--token.prism--boolean, .prism--token.prism--constant, .prism--token.prism--deleted, .prism--token.prism--number, .prism--token.prism--property, .prism--token.prism--symbol, .prism--token.prism--tag {
69+
color: #905
70+
}
71+
72+
.prism--token.prism--attr-name, .prism--token.prism--builtin, .prism--token.prism--char, .prism--token.prism--inserted, .prism--token.prism--selector, .prism--token.prism--string {
73+
color: #690
74+
}
75+
76+
.language-css .prism--token.prism--string, .style .prism--token.prism--string, .prism--token.prism--entity, .prism--token.prism--operator, .prism--token.prism--url {
77+
color: #9a6e3a;
78+
background: hsla(0, 0%, 100%, .5)
79+
}
80+
81+
.prism--token.prism--atrule, .prism--token.prism--attr-value, .prism--token.prism--keyword {
82+
color: #07a
83+
}
84+
85+
.prism--token.prism--class-name, .prism--token.prism--function {
86+
color: #dd4a68
87+
}
88+
89+
.prism--token.prism--important, .prism--token.prism--regex, .prism--token.prism--variable {
90+
color: #e90
91+
}
92+
93+
.prism--token.prism--bold, .prism--token.prism--important {
94+
font-weight: 700
95+
}
96+
97+
.prism--token.prism--italic {
98+
font-style: italic
99+
}
100+
101+
.prism--token.prism--entity {
102+
cursor: help
103+
}
104+
105+
div.code-toolbar {
106+
position: relative
107+
}
108+
109+
div.code-toolbar > .toolbar {
110+
position: absolute;
111+
z-index: 10;
112+
top: .3em;
113+
right: .2em;
114+
transition: opacity .3s ease-in-out;
115+
opacity: 0
116+
}
117+
118+
div.code-toolbar:hover > .toolbar {
119+
opacity: 1
120+
}
121+
122+
div.code-toolbar:focus-within > .toolbar {
123+
opacity: 1
124+
}
125+
126+
div.code-toolbar > .toolbar > .toolbar-item {
127+
display: inline-block
128+
}
129+
130+
div.code-toolbar > .toolbar > .toolbar-item > a {
131+
cursor: pointer
132+
}
133+
134+
div.code-toolbar > .toolbar > .toolbar-item > button {
135+
background: 0 0;
136+
border: 0;
137+
color: inherit;
138+
font: inherit;
139+
line-height: normal;
140+
overflow: visible;
141+
padding: 0;
142+
-webkit-user-select: none;
143+
-moz-user-select: none;
144+
-ms-user-select: none
145+
}
146+
147+
div.code-toolbar > .toolbar > .toolbar-item > a, div.code-toolbar > .toolbar > .toolbar-item > button, div.code-toolbar > .toolbar > .toolbar-item > span {
148+
color: #bbb;
149+
font-size: .8em;
150+
padding: 0 .5em;
151+
background: #f5f2f0;
152+
background: rgba(224, 224, 224, .2);
153+
box-shadow: 0 2px 0 0 rgba(0, 0, 0, .2);
154+
border-radius: .5em
155+
}
156+
157+
div.code-toolbar > .toolbar > .toolbar-item > a:focus, div.code-toolbar > .toolbar > .toolbar-item > a:hover, div.code-toolbar > .toolbar > .toolbar-item > button:focus, div.code-toolbar > .toolbar > .toolbar-item > button:hover, div.code-toolbar > .toolbar > .toolbar-item > span:focus, div.code-toolbar > .toolbar > .toolbar-item > span:hover {
158+
color: inherit;
159+
text-decoration: none
160+
}

src/psc/static/prism.js

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/psc/templates/example.jinja2

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
{% block extra_head %}
33
<meta name="subtitle" content="{{ subtitle }}">
44
<script defer src="{{ pyscript_url }}"></script>
5-
{{ extra_head | safe }}{% endblock %}
5+
{{ extra_head | safe }}
6+
{% endblock %}
67
{% block main %}
78
<main id="main_container" class="container">
9+
<a class="button is-pulled-right" href="./code.html">Show Code</a>
810
<h1 class="title">{{ title }}</h1>
911
{% if author %}
10-
<p>By <a href="../authors/{{ author.name }}.html">{{ author.title }}</a></p>
12+
<p>By <a href="../authors/{{ author.name }}.html">{{ author.title }}</a></p>
1113
{% endif %}
1214
<h1 class="subtitle">{{ subtitle }}</h1>
1315
<div class="content">{{ body | safe }}</div>

src/psc/templates/example_code.jinja2

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{% extends "layout.jinja2" %}
2+
{% block extra_head %}
3+
<script src="{{ root_path }}/static/prism.js"></script>
4+
<script defer>Prism.plugins.customClass.prefix("prism--");</script>
5+
<link rel="stylesheet" href="{{ root_path }}/static/prism.css">
6+
{% endblock %}
7+
{% block main %}
8+
<main id="main_container" class="container">
9+
<a class="button is-pulled-right" href="./index.html">Back to Demo</a>
10+
<h1 class="title">{{ title }}</h1>
11+
<div class="content">
12+
{% for linked_file in linked_files %}
13+
<h2>{{ linked_file.path.name }}</h2>
14+
<pre class="prism-code"><code class="language-{{ linked_file.language }}"
15+
data-prismjs-copy="Copy the snippet">{{ linked_file.body }}</code></pre>
16+
{% endfor %}
17+
</div>
18+
</main>
19+
{% endblock %}

tests/test_generic_example.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ def test_hello_world(client_page: PageT) -> None:
1010

1111
# Title and subtitle
1212
title = soup.select_one("title")
13-
assert title
14-
assert title.text == "Hello World | PyScript Collective"
13+
assert title and title.text == "Hello World | PyScript Collective"
1514
subtitle = soup.select_one('meta[name="subtitle"]')
1615
assert subtitle
1716
assert (
@@ -33,8 +32,27 @@ def test_hello_world(client_page: PageT) -> None:
3332
py_scripts = soup.select("py-script")
3433
assert len(py_scripts) == 1
3534

35+
# Back to code button
36+
button = soup.select_one("a.is-pulled-right")
37+
assert button and "Show Code" == button.text
38+
3639

3740
def test_hello_world_js(test_client: TestClient) -> None:
3841
"""Test the static assets for Hello World."""
3942
response = test_client.get("/gallery/examples/hello_world/hello_world.js")
4043
assert response.status_code == 200
44+
45+
46+
def test_hello_world_code(client_page: PageT) -> None:
47+
"""Test code samples for hello world example."""
48+
soup = client_page("/gallery/examples/hello_world/code.html")
49+
title = soup.select_one("title")
50+
assert title
51+
assert "Hello World Code | PyScript Collective" == title.text.strip()
52+
linked_file_heading = soup.select_one("h2")
53+
assert linked_file_heading
54+
assert "index.html" == linked_file_heading.text
55+
56+
# Back to code button
57+
button = soup.select_one("a.is-pulled-right")
58+
assert button and "Back to Demo" == button.text

0 commit comments

Comments
 (0)