Skip to content

Initialize shinyjson from shinychat with renames and hello world placeholders - #1

Closed
schloerke with Copilot wants to merge 2 commits into
mainfrom
copilot/rename-shinchat-to-shinyjson
Closed

schloerke with Copilot wants to merge 2 commits into
mainfrom
copilot/rename-shinchat-to-shinyjson

Conversation

Copilot AI commented Mar 2, 2026 •

Copy link
Copy Markdown

Bootstraps the shinyjson repo using posit-dev/shinychat as the template, renames all shinychat references to shinyjson, and strips business logic from R/Python/JS packages leaving minimal hello world placeholders.

Changes

Repository structure (from shinychat)

  • Root: LICENSE, Makefile, pyproject.toml, .gitignore, shinyjson.Rproj
  • .github/workflows/: R CMD check, pkgdown, py-check, py-release, quartodoc, verify-js-built
  • js/: build toolchain (esbuild, eslint, tsconfig, package.json)
  • pkg-py/: Python package scaffold
  • pkg-r/: R package scaffold

Renames

All shinychat → shinyjson throughout file contents and filenames (package names, URLs, descriptions, Makefile targets, DESCRIPTION, pyproject.toml, NAMESPACE, etc.).

Business logic → hello world placeholders

R — all chat logic removed; single placeholder:

# pkg-r/R/hello_world.R
#' @export
hello_world <- function() {
  "Hello, World!"
}

Python — all _chat*.py removed; minimal package:

# pkg-py/src/shinyjson/__init__.py
def hello_world() -> str:
    """Return a Hello, World! greeting."""
    return "Hello, World!"

JavaScript — chat/markdown-stream/utils removed; single entry point:

// js/src/shinyjson/shinyjson.ts
export function helloWorld(): string {
  return "Hello, World!"
}

build.ts updated to reference src/shinyjson/shinyjson.{ts,scss} instead of the removed chat/markdown-stream entries.

Security

Added permissions: contents: read to verify-js-built.yaml to scope GITHUB_TOKEN to minimum required.

Original prompt
  • Initialize the repo from https://github.com/posit-dev/shinychat
  • Rename all occurrences of shinchat to shinyjson
  • For the business logic in the R, python, and javascript folders... remove all of the logic files, but leave a "hello world" files as a place holder

✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

…world placeholders

Co-authored-by: schloerke <93231+schloerke@users.noreply.github.com>
Copilot AI changed the title [WIP] Rename occurrences of shinchat to shinyjson and remove logic files Initialize shinyjson from shinychat with renames and hello world placeholders Mar 2, 2026
@schloerke
schloerke marked this pull request as ready for review March 2, 2026 20:16
@schloerke

Copy link
Copy Markdown
Collaborator

Closing as implemented locally

@schloerke schloerke closed this Mar 5, 2026
@schloerke
schloerke deleted the copilot/rename-shinchat-to-shinyjson branch March 5, 2026 17:21
schloerke added a commit that referenced this pull request May 14, 2026
WeakKeyDictionary keyed only on the component instance kept the same
reactive.calc alive across sessions. Module-level components see many
sessions; the calc bound to session #1 is destroyed when that session
ends, and session #2 hit a DestroyedReactiveError when re-using the
cached calc -> grey 'Disconnected' overlay.

Switch to a per-instance attribute that stores (session_obj, calc).
If the captured session differs from the current one, recreate the calc.
schloerke added a commit that referenced this pull request May 14, 2026
* docs(specs): shinyui metadata-consolidation prototype design (#69)

Stage A design for umbrella #68 / issue #69. Specs a new sibling
Python package `shinyui` at pkg-py/src/shinyui/ that prototypes a
class-per-component UI hierarchy with seven concrete archetypes,
resolves the three umbrella open questions (handler registration,
bookmark lookup, update signature), and refactors the umbrella's
UiInput/UiLayout straddler into orthogonal HasInputValue + Updatable
mixins so layouts-with-state read honestly.

* docs(plans): shinyui metadata-consolidation implementation plan (#69)

* feat(shinyui): scaffold sibling package + test infra

* style(shinyui): ruff format conftest and smoke test

* feat(shinyui): UiComponent base + session/read helpers

* feat(shinyui): AllowsChildren mixin

Adds AllowsChildren mixin (pkg-py/src/shinyui/_children.py) with children
list, append(), __enter__/__exit__ overrides. Also fixes UiComponent.__init__
to forward *args cooperatively so AllowsChildren receives positional children
when MRO order is MyComp(UiComponent, AllowsChildren).

* feat(shinyui): per-session id->instance registry

* feat(shinyui): HasInputValue mixin + handler registration

* feat(shinyui): Updatable abstract mixin

* feat(shinyui): UiInput / UiOutput / UiLayout role classes

* feat(shinyui): local reactive_calc_method helper

* feat(shinyui): UiInputSlider + input_slider() factory

* feat(shinyui): UiInputSelect + input_select() factory

* feat(shinyui): UiOutputCode + output_code() factory

* feat(shinyui): UiOutputPlot with read-only signal accessors

* feat(shinyui): UiAccordionPanel + accordion_panel() factory

* feat(shinyui): UiAccordion + accordion() factory

* feat(shinyui): UiCard with full_screen_value() and update()

* feat(shinyui): public exports + accordion pyright fixes

* test(shinyui): cross-cutting hierarchy + lifecycle tests

* test(shinyui): bookmark id->instance round-trip

* feat(shinyui): lookup_component + example app exercising the full reference set

* fix(shinyui): recursively tagify subtrees in container classes

htmltools' built-in Tag.tagify walks only one level: it replaces direct
Tagifiable children with their tagify() result but does not recurse into
the resulting Tag's own children. Containers whose tagify wraps un-resolved
Tagifiable descendants therefore reached htmltools' rendering layer with
nested Tagifiables, triggering 'non-tagified object' at render time.

Add UiComponent._deep_tagify(node) — a small recursive helper that walks
Tag/TagList children, calling tagify() on every Tagifiable it encounters.
Apply it in UiCard and UiAccordion (the two containers whose children can
themselves be Tagifiable). The example app now renders successfully.

* fix(shinyui,example): card full_screen reads _full_screen suffix; module-level demo components

- UiCard.full_screen_value() now reads input.<id>_full_screen (matches
  Shiny's card binding wire format) instead of input.<id>. Update card
  and read-accessors tests to match.
- Example app 14 constructs components at module level so closures
  share them between app_ui (HTTP phase, no session) and server
  (WebSocket phase, session bound). The lookup_component approach in
  the previous version returned None during HTTP-phase construction,
  crashed the session, and showed the grey 'Disconnected' overlay.
- Replace matplotlib placeholder with a PIL solid-color image so the
  example runs without matplotlib in the venv.

* feat(example): visible plot placeholder for app-py/14

Solid-color placeholder was indistinguishable from the card background.
Added a border, crosshair, and label text via PIL.ImageDraw so the plot
output has a clear visual target for click and brush interactions.

* chore: gitignore stray screenshot from local playwright run

* fix(shinyui): cross-session reactive_calc_method cache invalidation

WeakKeyDictionary keyed only on the component instance kept the same
reactive.calc alive across sessions. Module-level components see many
sessions; the calc bound to session #1 is destroyed when that session
ends, and session #2 hit a DestroyedReactiveError when re-using the
cached calc -> grey 'Disconnected' overlay.

Switch to a per-instance attribute that stores (session_obj, calc).
If the captured session differs from the current one, recreate the calc.

* feat(example): real scatter plot driven by slider/select/seed reads

* feat(example): two-button accordion control; sample/dist/seed live in Settings panel

* feat(example): move summary code block into Diagnostics accordion panel

* feat(shinyui): UiInputActionButton class + integrate into example 14

UiInputActionButton(UiInput, Updatable) ships the same shape as the
other input classes:
- typed __init__ mirroring shiny.ui.input_action_button (label, icon,
  width, disabled)
- .count() reactive accessor exposing the click counter (0 before
  first click)
- typed update(*, label, icon, disabled) delegating to
  shiny.ui.update_action_button
- factory function input_action_button(...)
- 6 unit tests pinning factory/snapshot/count/update behavior

Example 14 swaps ui.input_action_button for su.input_action_button on
both Open-all and Close-all controls and wires the @reactive.event
deps via btn.count instead of input.<id>, so the demo's button surface
is fully class-based now.

* feat(shinyui): rename UiOutputPlot.dblclick_value -> dbl_value

Matches the shorter accessor name and reads the same _dblclick wire
suffix. Add module-docstring note that limits_value / selection_value
accessors are intentionally absent: shiny.ui.output_plot only pushes
the four documented interaction signals (click, dblclick, hover, brush).
If shiny gains _limits or _selection upstream, add the accessors then.

* feat(example): rewrite example 14 as a Shiny Express app

Switch from def app_ui(request) / def server() to a top-level expressify
script. Construct shinyui components programmatically (factory calls
with children as args) since AllowsChildren is not wired into Express's
RecallContextManager yet — sub-issue 3 territory.

Wrap the @render decorators in 'with ui.hold():' to suppress Express's
auto-placement; the renderers still register with the session by id,
binding to the output_code / output_plot elements we placed inside the
accordion. Without hold(), Express would inject duplicate <pre id=...>
elements at the page tail.

Use plain shiny.ui.layout_column_wrap (imported as _sui) for the inline
button row; shiny.express.ui.layout_column_wrap is the recall-context
variant that takes 0 positional args.

* feat(shinyui): rename UiInputActionButton.count -> clicked

Reads more naturally at the call site ("btn.clicked() > 0" vs the
slightly ambiguous "btn.count() > 0"). Wire suffix unchanged
(reads input.<id> directly). Tests and example 14 updated.

* feat(shinyui): demo __init_subclass__ handler registration on UiInputActionButton

Most shinyui classes register input handlers via an explicit
cls._register_input_handler() call at module load. The action button now
demonstrates the alternative: a small _InputHandlerAutoRegister mixin
whose __init_subclass__ hook auto-fires registration when the class is
defined.

Concretely:
- Add input_handler_name = 'shinyui.action' and a small _input_handler
  staticmethod that coerces wire value -> int (parallel of py-shiny's
  'shiny.action' handler).
- Register under 'shinyui.action' (not 'shiny.action') so we don't
  collide with shiny's built-in; the demo's purpose is the registration
  mechanism, not real wire traffic — shiny's markup still routes
  action-button events through its own 'shiny.action' handler.
- Pin behavior with two new tests: registry contains 'shinyui.action'
  after import, and _input_handler returns plain ints. Update the
  cross-cutting test_input_handler_registration.py to reflect the
  exception.

* refactor(shinyui): drop _deep_tagify; rely on .tagify() chain resolution

htmltools' Tag.tagify() iterates Tagifiable->Tagifiable chains inside its
single-level TagList walk, so calling .tagify() once on the outer Tag is
enough to fully resolve our Tagifiable descendants. The custom recursive
_deep_tagify helper I'd added is unnecessary.

UiAccordion still pre-resolves its children via [c.tagify() for c in ...]
because shiny.ui.accordion does an explicit isinstance(panel, AccordionPanel)
check on positional args and rejects UiAccordionPanel (which is Tagifiable
but not an AccordionPanel subclass). UiCard has no such isinstance check,
so it hands its children in unchanged and lets the outer .tagify() resolve
them.

Both files document the rule inline; the module-docstring of _base.py adds
a one-line guidance pointer for future container subclasses.

* refactor(shinyui): move __init_subclass__ auto-register onto HasInputValue

The standalone _InputHandlerAutoRegister mixin duplicated logic that
HasInputValue already owns (input_handler_name, _input_handler,
_register_input_handler classmethod). Move the __init_subclass__ hook
onto HasInputValue itself — now every HasInputValue subclass auto-fires
registration on class-definition. Classes with the defaults
(input_handler_name='' or _input_handler is None) skip silently, so
this is no-op for slider/select/card/accordion and active only for
UiInputActionButton.

Drops the extra base from UiInputActionButton's MRO and removes ~20
lines of plumbing. The action-button module docstring still explains
the demo and the 'shinyui.action' vs 'shiny.action' separation.

* refactor(shinyui): rename concrete classes to snake_case

Match shiny.render.* convention (e.g. shiny.render.data_frame) by
naming concrete user-facing classes in snake_case. Drops the parallel
factory functions since the class name now equals the call-site name —
no more double record keeping.

Bases and mixins (UiComponent, UiInput, UiOutput, UiLayout,
HasInputValue, Updatable, AllowsChildren) stay PascalCase, mirroring
shiny.render.Renderer.

Class rename map:
  UiInputSlider       -> input_slider
  UiInputSelect       -> input_select
  UiInputActionButton -> input_action_button
  UiOutputCode        -> output_code
  UiOutputPlot        -> output_plot
  UiCard              -> card
  UiAccordion         -> accordion
  UiAccordionPanel    -> accordion_panel

Docs (spec + plan) updated to reflect the new names; the older
2026-05-06 umbrella spec is left alone since it describes the original
design vision.

* feat(shinyui): add Express/Core overloads to container classes

The three classes with AllowsChildren (card, accordion, accordion_panel)
now expose two @overload signatures on __init__:

1. Express overload (no positional children) — listed first so IDEs
   prefer it when the user writes `with card(id=...) as c: ...`.
2. Core overload (positional children) — for inline construction like
   `card(child_a, child_b, id=...)`.

The runtime __init__ is unchanged; the overloads are pure type hints
for IDE / pyright consumption, mirroring the umbrella spec's sub-issue
2 plan for Core/Express signature unification.

* docs(shinyui): add class + __init__ docstrings to concrete UI classes

Each of the eight concrete classes (input_slider, input_select,
input_action_button, output_code, output_plot, card, accordion,
accordion_panel) now ships:

- A class-level docstring naming the wire id, the matching class
  accessor (if any), and a small idiomatic Example block.
- An __init__ docstring documenting id/label (or title), the key
  shape parameters, and a pointer to shiny.ui.<name> for the
  long tail of pass-through kwargs.

Pure documentation — no signature or runtime behaviour changes.
input_slider already had this treatment; the others now match its
template.

* refactor(shinyui): accordion_panel.tagify() returns Tag; drop type:ignore

Honor the UiComponent.tagify() -> Tag contract on accordion_panel by
chaining .tagify() on shiny's AccordionPanel wrapper. The previous
override that returned AccordionPanel (a Tagifiable, not a Tag) needed
a # type: ignore[override] annotation; that's now gone.

Two coupling points required care:

- shiny.ui.accordion does an explicit isinstance(panel, AccordionPanel)
  check on its positional args, so the parent accordion can't consume
  our rendered Tag. Add a small private helper _build_accordion_panel()
  that returns the AccordionPanel wrapper; the accordion's tagify()
  uses that helper instead of calling child.tagify().

- shiny's AccordionPanel.tagify() raises if _accordion_id is not set
  (normally written by the parent accordion). For standalone rendering
  (snapshot tests, ad-hoc inspection), stamp a placeholder id keyed off
  the panel's value before calling .tagify(). The parent path is
  unaffected — it gets a fresh AccordionPanel each time via the helper.

Snapshot test for accordion_panel switched from attribute comparison
(_title, _args, _data_value, _icon) to asserting the returned object
isinstance(Tag) with the title and body content present in the
rendered HTML.

* docs(shinyui): move container docstrings onto @overload signatures

card, accordion, and accordion_panel each have an Express overload (no
positional children, used in 'with X():' blocks) and a Core overload
(inline positional children). Move the doc + Example block onto each
overload separately so IDE tooltips show the right idiom for the call
site. The implementation __init__ at the bottom of each class loses its
docstring (overload stubs carry the docs now).

* refactor(shinyui): accordion_panel.tagify() returns Tag; drop _build_accordion_panel helper

Honor the UiComponent.tagify() -> Tag contract on accordion_panel by
chaining .tagify() on shiny's AccordionPanel wrapper. Stamp a placeholder
_accordion_id so standalone .tagify() works outside a parent accordion
(shiny's AccordionPanel.tagify() raises if _accordion_id is unset).

The parent accordion now builds AccordionPanel wrappers inline from each
child's stored title/children/_value/icon — rather than calling a helper
method on the child — since shiny.ui.accordion does an isinstance check
on positional args and rejects rendered Tags. No _build_accordion_panel
indirection.

* style(shinyui): package-wide N801 ignore; drop per-line noqa pragmas

Concrete shinyui classes are intentionally snake_case (matching
shiny.render.data_frame's convention — class name == call-site name,
no parallel factory functions). Configure ruff to ignore N801
('class name should use CapWords') package-wide for pkg-py/src/shinyui
in pyproject.toml, and remove the eight per-line '# noqa: N801'
pragmas the concrete classes carried.

* docs: README + spec fixes from Copilot PR review

Addresses PR #100 review threads from Copilot:

- examples/.../README.md: full rewrite for the current Express app —
  drops the stale lookup_component snippet, the removed
  _auto_expand_at_high_n effect, and the n>800 'What to try' bullet.
  Adds the Open all / Close all button section that's actually wired
  in app.py. Corrects the card full_screen wire id to _full_screen.

- docs/.../spec: 'seven concrete classes' -> 'at least seven', and
  the lifecycle bullet for handler registration now describes the
  __init_subclass__ approach actually used (HasInputValue auto-fires
  cls._register_input_handler() on subclass creation), with
  input_action_button called out as the demonstrating class.

- pkg-py/src/shinyui/_card.py: module docstring corrected to say the
  accessor reads input.<id>_full_screen (the actual wire suffix) —
  the previous text contradicted full_screen_value()'s implementation.

- pkg-py/src/shinyui/_reactive.py: implement the on_ended cache
  eviction the module docstring already promised. The cached
  (session, calc) attribute is now dropped proactively when the
  captured session ends, before the lazy mismatch-detection at the
  next accessor call would have replaced it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants