Skip to content

Commit be6985a

Browse files
committed
Merge branch 'main' into machow-feat-narwhals
* main: chore: Fix white space formatting in `reactive.poll` Returns (posit-dev#1667) fix(shiny create): Use `template.id` for default directory (posit-dev#1666) fix(tests): Add missing env vars for shinyapps.io tests (posit-dev#1663) v1.1.0 release candidate Put streaming visual cue at the end of the content (posit-dev#1659) CI(debug): Print full stdout/stderr when command fails (posit-dev#1660)
2 parents 1155868 + a7e8b03 commit be6985a

File tree

13 files changed

+103
-75
lines changed

13 files changed

+103
-75
lines changed

.github/workflows/deploy-tests.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ jobs:
6464
pypi_shiny: false
6565
pypi_rsconnect: false
6666
base_test_dir: "./tests/playwright/deploys"
67+
app_name: "express_page_sidebar"
6768
test_shinyappsio: true
6869

6970
fail-fast: false
@@ -110,9 +111,9 @@ jobs:
110111
DEPLOY_APPS: "true"
111112
DEPLOY_CONNECT_SERVER_URL: "${{ (matrix.config.released_connect_server && 'https://connect.posit.it/') || 'https://rsc.radixu.com/' }}"
112113
DEPLOY_CONNECT_SERVER_API_KEY: "${{ (matrix.config.released_connect_server && secrets.DEPLOY_CONNECT_POSIT_SERVER_API_KEY) || secrets.DEPLOY_CONNECT_SERVER_API_KEY }}"
113-
DEPLOY_SHINYAPPS_NAME: "${{ matrix.config.test_shinyappsio && matrix.config.shinyapps_name }}"
114-
DEPLOY_SHINYAPPS_TOKEN: "${{ matrix.config.test_shinyappsio && matrix.config.shinyapps_token }}"
115-
DEPLOY_SHINYAPPS_SECRET: "${{ matrix.config.test_shinyappsio && matrix.config.shinyapps_secret }}"
114+
DEPLOY_SHINYAPPS_NAME: "${{ matrix.config.test_shinyappsio && secrets.DEPLOY_SHINYAPPS_NAME }}"
115+
DEPLOY_SHINYAPPS_TOKEN: "${{ matrix.config.test_shinyappsio && secrets.DEPLOY_SHINYAPPS_TOKEN }}"
116+
DEPLOY_SHINYAPPS_SECRET: "${{ matrix.config.test_shinyappsio && secrets.DEPLOY_SHINYAPPS_SECRET }}"
116117
EXPRESS_PAGE_SIDEBAR_NAME: "${{ matrix.config.app_name }}"
117118
DEPLOY_GITHUB_REQUIREMENTS_TXT: "${{ !matrix.config.pypi_shiny }}"
118119
timeout-minutes: 30

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [UNRELEASED]
99

10+
### Bug fixes
11+
12+
* `shiny create` now uses the template `id` rather than the directory name as the default directory. (#1666)
13+
14+
## [1.1.0] - 2024-09-03
15+
1016
### New features
1117

1218
* `ui.Chat()` gains a new `.update_user_input()` method, which adds the ability to update the input placeholder message. As a result, `.set_user_message()` is now deprecated (since the new method can also be used to update the message). (#1594)

Makefile

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,21 @@ clean-js: FORCE
149149
SUB_FILE:=
150150
PYTEST_BROWSERS:= --browser webkit --browser firefox --browser chromium
151151
PYTEST_DEPLOYS_BROWSERS:= --browser chromium
152+
153+
# Full test path to playwright tests
154+
TEST_FILE:=tests/playwright/$(SUB_FILE)
155+
# Default `make` values that shouldn't be directly used; (Use `TEST_FILE` instead!)
156+
DEPLOYS_TEST_FILE:=tests/playwright/deploys$(SUB_FILE)
157+
SHINY_TEST_FILE:=tests/playwright/shiny/$(SUB_FILE)
158+
EXAMPLES_TEST_FILE:=tests/playwright/examples/$(SUB_FILE)
159+
152160
install-playwright: FORCE
153161
playwright install --with-deps
154162

155163
install-rsconnect: FORCE
156164
pip install git+https://github.com/rstudio/rsconnect-python.git#egg=rsconnect-python
157165

158-
# Full test path to playwright tests
159-
TEST_FILE:="tests/playwright/$(SUB_FILE)"
166+
160167
# All end-to-end tests with playwright
161168
playwright: install-playwright ## All end-to-end tests with playwright; (TEST_FILE="" from root of repo)
162169
pytest $(TEST_FILE) $(PYTEST_BROWSERS)
@@ -169,20 +176,18 @@ playwright-show-trace: ## Show trace of failed tests
169176

170177
# end-to-end tests with playwright; (SUB_FILE="" within tests/playwright/shiny/)
171178
playwright-shiny: FORCE
172-
$(MAKE) playwright TEST_FILE="tests/playwright/shiny/$(SUB_FILE)"
179+
$(MAKE) playwright TEST_FILE="$(SHINY_TEST_FILE)"
173180

174181
# end-to-end tests on deployed apps with playwright; (SUB_FILE="" within tests/playwright/deploys/)
175182
playwright-deploys: FORCE
176-
$(MAKE) playwright PYTEST_BROWSERS="$(PYTEST_DEPLOYS_BROWSERS)" TEST_FILE="$(TEST_FILE)"
177-
playwright-deploys-legacy: FORCE
178-
$(MAKE) playwright TEST_FILE="tests/playwright/deploys/$(SUB_FILE)" PYTEST_BROWSERS="$(PYTEST_DEPLOYS_BROWSERS)"
183+
$(MAKE) playwright PYTEST_BROWSERS="$(PYTEST_DEPLOYS_BROWSERS)" TEST_FILE="$(DEPLOYS_TEST_FILE)"
179184

180185
# end-to-end tests on all py-shiny examples with playwright; (SUB_FILE="" within tests/playwright/examples/)
181186
playwright-examples: FORCE
182-
$(MAKE) playwright TEST_FILE="tests/playwright/examples/$(SUB_FILE)"
187+
$(MAKE) playwright TEST_FILE="$(EXAMPLES_TEST_FILE)"
183188

184189
coverage: FORCE ## check combined code coverage (must run e2e last)
185-
pytest --cov-report term-missing --cov=shiny tests/pytest/ tests/playwright/shiny/$(SUB_FILE) $(PYTEST_BROWSERS)
190+
pytest --cov-report term-missing --cov=shiny tests/pytest/ $(SHINY_TEST_FILE) $(PYTEST_BROWSERS)
186191
coverage html
187192
$(BROWSER) htmlcov/index.html
188193

js/chat/chat.scss

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,6 @@ shiny-chat-container {
4040
.message-content {
4141
align-self: center;
4242
}
43-
.message-streaming-icon {
44-
display: none;
45-
opacity: 0;
46-
}
47-
&[streaming] .message-streaming-icon {
48-
display: block;
49-
animation-delay: 2s;
50-
animation-name: fade-in;
51-
animation-duration: 10ms;
52-
animation-fill-mode: forwards;
53-
}
5443
}
5544

5645
/* Align the user message to the right */
@@ -154,13 +143,3 @@ pre:has(.code-copy-button) {
154143
background-color: var(--bs-success, #198754);
155144
}
156145
}
157-
158-
/* Keyframes for the fading spinner */
159-
@keyframes fade-in {
160-
0% {
161-
opacity: 0;
162-
}
163-
100% {
164-
opacity: 1;
165-
}
166-
}

js/chat/chat.ts

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,17 @@ const ICONS = {
5757
// https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-css/3-dots-fade.svg
5858
dots_fade:
5959
'<svg width="24" height="24" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_S1WN{animation:spinner_MGfb .8s linear infinite;animation-delay:-.8s}.spinner_Km9P{animation-delay:-.65s}.spinner_JApP{animation-delay:-.5s}@keyframes spinner_MGfb{93.75%,100%{opacity:.2}}</style><circle class="spinner_S1WN" cx="4" cy="12" r="3"/><circle class="spinner_S1WN spinner_Km9P" cx="12" cy="12" r="3"/><circle class="spinner_S1WN spinner_JApP" cx="20" cy="12" r="3"/></svg>',
60-
// https://github.com/n3r4zzurr0/svg-spinners/blob/main/svg-css/bouncing-ball.svg
61-
ball_bounce:
62-
'<svg width="24" height="24" fill="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><style>.spinner_rXNP{animation:spinner_YeBj .8s infinite; opacity:.8}@keyframes spinner_YeBj{0%{animation-timing-function:cubic-bezier(0.33,0,.66,.33);cy:5px}46.875%{cy:20px;rx:4px;ry:4px}50%{animation-timing-function:cubic-bezier(0.33,.66,.66,1);cy:20.5px;rx:4.8px;ry:3px}53.125%{rx:4px;ry:4px}100%{cy:5px}}</style><ellipse class="spinner_rXNP" cx="12" cy="5" rx="4" ry="4"/></svg>',
60+
dot: '<svg width="12" height="12" xmlns="http://www.w3.org/2000/svg" class="chat-streaming-dot" style="margin-left:.25em;margin-top:-.25em"><circle cx="6" cy="6" r="6"/></svg>',
6361
};
6462

63+
function createSVGIcon(icon: string): HTMLElement {
64+
const parser = new DOMParser();
65+
const svgDoc = parser.parseFromString(icon, "image/svg+xml");
66+
return svgDoc.documentElement;
67+
}
68+
69+
const SVG_DOT = createSVGIcon(ICONS.dot);
70+
6571
const requestScroll = (el: HTMLElement, cancelIfScrolledUp = false) => {
6672
el.dispatchEvent(
6773
new CustomEvent("shiny-chat-request-scroll", {
@@ -127,17 +133,29 @@ class ChatMessage extends LightElement {
127133
return html`
128134
<div class="message-icon">${unsafeHTML(icon)}</div>
129135
<div class="message-content">${content}</div>
130-
<div class="message-streaming-icon">${unsafeHTML(ICONS.ball_bounce)}</div>
131136
`;
132137
}
133138

134139
updated(changedProperties: Map<string, unknown>): void {
135140
if (changedProperties.has("content")) {
136141
this.#highlightAndCodeCopy();
142+
if (this.streaming) this.#appendStreamingDot();
137143
// It's important that the scroll request happens at this point in time, since
138144
// otherwise, the content may not be fully rendered yet
139145
requestScroll(this, this.streaming);
140146
}
147+
if (changedProperties.has("streaming")) {
148+
this.streaming ? this.#appendStreamingDot() : this.#removeStreamingDot();
149+
}
150+
}
151+
152+
#appendStreamingDot(): void {
153+
const content = this.querySelector(".message-content") as HTMLElement;
154+
content.lastElementChild?.appendChild(SVG_DOT);
155+
}
156+
157+
#removeStreamingDot(): void {
158+
this.querySelector(".message-content svg.chat-streaming-dot")?.remove();
141159
}
142160

143161
// Highlight code blocks after the element is rendered
@@ -416,6 +434,7 @@ class ChatContainer extends LightElement {
416434
lastMessage.setAttribute("content", message.content);
417435

418436
if (message.chunk_type === "message_end") {
437+
this.lastMessage?.removeAttribute("streaming");
419438
this.#finalizeMessage();
420439
}
421440
}
@@ -441,7 +460,6 @@ class ChatContainer extends LightElement {
441460

442461
#finalizeMessage(): void {
443462
this.input.disabled = false;
444-
this.lastMessage?.removeAttribute("streaming");
445463
}
446464

447465
#onRequestScroll(event: CustomEvent<requestScrollEvent>): void {

shiny/_main_create.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,6 @@ def app_template_questions(
657657
mode: Optional[str] = None,
658658
dest_dir: Optional[Path] = None,
659659
):
660-
template_dir = template.path
661660
template_cli_name = cli_bold(cli_field(template.title or template.id))
662661

663662
if mode == "express" and not template.express_available:
@@ -666,7 +665,7 @@ def app_template_questions(
666665
)
667666

668667
click.echo(cli_wait(f"Creating {template_cli_name} Shiny app..."))
669-
dest_dir = directory_prompt(dest_dir, template_dir.name)
668+
dest_dir = directory_prompt(dest_dir, template.id)
670669

671670
if mode is None and template.express_available:
672671
mode = questionary.select(

shiny/reactive/_poll.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,10 @@ def poll(
8787
-------
8888
:
8989
A decorator that should be applied to a no-argument function that (expensively)
90-
reads whatever data is desired. (This function may be a regular function or a
91-
co-routine function.) The result of the decorator is a reactive
92-
:func:`~shiny.reactive.calc` that always returns up-to-date data, and invalidates
93-
callers when changes are detected via polling.
90+
reads whatever data is desired. (This function may be a regular function or a
91+
co-routine function.) The result of the decorator is a reactive
92+
:func:`~shiny.reactive.calc` that always returns up-to-date data, and
93+
invalidates callers when changes are detected via polling.
9494
9595
See Also
9696
--------

0 commit comments

Comments
 (0)