modFramesUI <- function(id) {
ns <- shiny::NS(id)
shiny::tagList(
shiny::includeCSS(system.file("css/modFrames-inline.css", package = "modFrames")),
shiny::includeCSS(system.file("css/modFrames-styles.css", package = "modFrames")),
shiny::includeScript(system.file("js/modFrames-tooltips.js", package = "modFrames")),
# ... dark mode toggle ...
playTimelapseUI(ns("timelapse")),
compareFramesUI(ns("compframes"))
)
}Module Architecture
modFrames follows a hierarchical module pattern with a main entry point that delegates to specialized sub-modules.
Structure Overview
modFramesUI() / modFramesServer()
|
├── playTimelapseUI() / playTimelapseServer()
│ └── Timelapse playback with animation controls
│
└── compareFramesUI() / compareFramesServer()
├── Journey Story Card (storytelling insights)
├── Frame Comparison Card (A/B comparison + diff)
└── generateJourneyInsights() / plotCreativeJourney()
Main Module: modFrames
modFramesUI()
The main UI function assembles the complete interface:
-
CSS Stylesheets:
inst/css/modFrames-inline.cssandinst/css/modFrames-styles.css -
JavaScript:
inst/js/modFrames-tooltips.jsfor Bootstrap 5 tooltips - Slider Skin: Custom shinyWidgets slider styling
- Dark Mode Toggle: Fixed-position light/dark switch (bslib)
- Sub-Module UIs: Timelapse controls and comparison cards
modFramesServer()
The main server function is minimal - it simply initializes sub-modules:
modFramesServer <- function(id, r) {
shiny::moduleServer(id, function(input, output, session) {
playTimelapseServer("timelapse", r)
compareFramesServer("compframes", r)
})
}Sub-Module: playTimelapse
Handles animated timelapse playback of artwork creation.
UI Components
- Slider with Animation: Frame selector with play/pause buttons
- Metrics Row: Total strokes, elapsed minutes, unique colors
- Frame Display: Current frame image from CDN
- Color Graph: Per-frame color composition visualization
Server Logic
- Extracts UUIDs from
r$appdata$artwork$stats - Reads
frame_statsfor metric calculations - Computes cumulative unique colors using
colors_added - Renders frame images via
artutils::path_replay_frame() - Renders color graphs via
artutils::path_replay_graph()
Key Reactives
Sub-Module: compareFrames
Handles side-by-side frame comparison with difference visualization and storytelling insights.
UI Components
-
Journey Summary Card:
- Journey insights text (left column, 7/12 width)
- Plotly visualization (right column, 5/12 width)
-
Frame Comparison Card:
- Dual-handle slider for selecting Frame A and Frame B
- Contextual insights (alerts for phase changes, color expansions)
- Three-column display: Frame A, Frame B, Difference
Server Logic
-
Journey Insights: Generates once when data loads
r_journey_insights <- reactive({ dt <- r_frame_stats() generateJourneyInsights(dt) }) Frame Selection: Updates slider range based on total frames
Frame Rendering: Fetches images from CDN for A and B
-
Difference Image: Creates via ImageMagick comparison
write_diff <- function(img_a, img_b, outpath) { magick::image_compare(img_b, img_a, fuzz = 5) |> magick::image_transparent("#cccccc", fuzz = 5) |> magick::image_write(outpath, format = "png") } -
Contextual Insights: Dynamic alerts based on frame selection
- Major color expansion (>50 colors added)
- Phase transitions
- Peak creative intensity (palette_change_score > 0.7)
- Sustained effort (>1 hour between frames)
Data Flow
artutils::get_appdata()
|
v
r$appdata$artwork$frame_stats (21 columns)
|
+---> playTimelapseServer
| - Reads elapsed_minutes, cumulative_strokes, unique_colors
| - Computes cumulative_colors from colors_added
|
+---> compareFramesServer
|
+---> generateJourneyInsights(frame_stats)
| - Analyzes color approach, phases, complexity
| - Returns structured insights object
|
+---> plotCreativeJourney(frame_stats, insights)
- Creates Plotly visualization
- Shows color evolution with phase boundaries
Helper Functions
info_icon_tooltip()
Creates Bootstrap 5 tooltip icons for contextual help:
info_icon_tooltip <- function(tooltip_text, placement = "top") {
icon_element <- shiny::icon("info-circle", class = "mod-frames-info-icon")
icon_element$attribs$`data-bs-toggle` <- "tooltip"
icon_element$attribs$title <- tooltip_text
icon_element
}metric_block()
Renders consistent metric displays with optional icons:
metric_block <- function(value, label, icon = NULL, highlight = FALSE) {
# Supports both static values and Shiny outputs (textOutput)
# Returns styled div with icon, value, and label
}Styling
CSS Files
-
inst/css/modFrames-inline.css: Inline critical styles -
inst/css/modFrames-styles.css: Component styles
CSS Classes
| Class | Purpose |
|---|---|
.mod-frames-card |
Base card styling |
.mod-frames-journey-card |
Journey summary card |
.mod-frames-compare-card |
Frame comparison card |
.mod-frames-timelapse-card |
Timelapse controls |
.mod-frames-frame-card |
Individual frame display |
.mod-frames-metrics-row |
Horizontal metrics layout |
.mod-frames-metric |
Individual metric block |
.mod-frames-info-icon |
Tooltip info icons |
Extension Points
Adding New Metrics
To display additional frame_stats columns:
- Access the column in the relevant server function
- Add a
metric_block()call in the UI render function - Style with existing CSS classes
Custom Visualizations
To add new Plotly charts:
- Create a new
plotlyOutput()in the UI - Render using
r_frame_stats()reactive - Follow the pattern in
plotCreativeJourney()
Next Steps
- Storytelling Engine - Deep dive into journey insights
- Function Reference - Complete API documentation
