diff --git a/.DS_Store b/.DS_Store
deleted file mode 100644
index 62998f5..0000000
Binary files a/.DS_Store and /dev/null differ
diff --git a/.Rbuildignore b/.Rbuildignore
index 89b4b32..cd5fb5e 100644
--- a/.Rbuildignore
+++ b/.Rbuildignore
@@ -15,5 +15,6 @@
^cran-comments\.md$
^CRAN-SUBMISSION$
^\.claude$
+^CLAUDE\.md$
^\.Rhistory$
^\.positai$
diff --git a/.Rhistory b/.Rhistory
deleted file mode 100644
index ec86f90..0000000
--- a/.Rhistory
+++ /dev/null
@@ -1,281 +0,0 @@
-pkgdown::build_site()
-pkgdown::build_site()
-library(jsonlite)
-library(dplyr)
-library(tibble)
-library(lubridate)
-# ── Sample data (57 employees) ────────────────────────────────────────────────
-json_url <- "https://raw.githubusercontent.com/StrategicProjects/DT2/main/inst/examples/employees.json"
-# For offline use, the same JSON is bundled in inst/examples/employees.json
-json_txt <- '{
-"data": [
-{"name":"Tiger Nixon","position":"System Architect","salary":"320800","start_date":"2011-04-25","office":"Edinburgh","extn":"5421"},
-{"name":"Garrett Winters","position":"Accountant","salary":"170750","start_date":"2011-07-25","office":"Tokyo","extn":"8422"},
-{"name":"Ashton Cox","position":"Junior Technical Author","salary":"86000","start_date":"2009-01-12","office":"San Francisco","extn":"1562"},
-{"name":"Cedric Kelly","position":"Senior JavaScript Developer","salary":"433060","start_date":"2012-03-29","office":"Edinburgh","extn":"6224"},
-{"name":"Airi Satou","position":"Accountant","salary":"162700","start_date":"2008-11-28","office":"Tokyo","extn":"5407"},
-{"name":"Brielle Williamson","position":"Integration Specialist","salary":"372000","start_date":"2012-12-02","office":"New York","extn":"4804"},
-{"name":"Herrod Chandler","position":"Sales Assistant","salary":"137500","start_date":"2012-08-06","office":"San Francisco","extn":"9608"},
-{"name":"Rhona Davidson","position":"Integration Specialist","salary":"327900","start_date":"2010-10-14","office":"Tokyo","extn":"6200"},
-{"name":"Colleen Hurst","position":"JavaScript Developer","salary":"205500","start_date":"2009-09-15","office":"San Francisco","extn":"2360"},
-{"name":"Sonya Frost","position":"Software Engineer","salary":"103600","start_date":"2008-12-13","office":"Edinburgh","extn":"1667"}
-]
-}'
-df <- fromJSON(json_txt, flatten = TRUE)$data %>%
-as_tibble() %>%
-mutate(
-salary = as.numeric(salary),
-extn = as.integer(extn),
-start_date = ymd(start_date)
-)
-# ── Shiny App ─────────────────────────────────────────────────────────────────
-library(shiny)
-library(bslib)
-library(DT2)
-library(htmlwidgets)
-ui <- page_sidebar(
-theme = bs_theme(version = 5, bootswatch = "spacelab"),
-title = "DT2 — Complete Example",
-sidebar = sidebar(
-h5("Features"),
-tags$ul(
-tags$li("ColumnControl (order + search dropdowns)"),
-tags$li("Export buttons with spacer"),
-tags$li("Custom JS renderers"),
-tags$li("pt-BR translation")
-)
-),
-# Flag sprites
-tags$head(
-tags$link(
-rel = "stylesheet", type = "text/css",
-href = "https://cdn.jsdelivr.net/gh/lafeber/world-flags-sprite/stylesheets/flags32-both.css"
-),
-tags$style(HTML("
-.f32 .flag { display:inline-block; width:32px; height:32px;
-vertical-align:middle; margin-right:6px; }
-table.dataTable tbody td { vertical-align: middle; }
-"))
-),
-card(
-card_header("Employee Table"),
-card_body(dt2_output("tbl", height = "auto"))
-)
-)
-server <- function(input, output, session) {
-output$tbl <- render_dt2({
-# ── JS Renderers ──────────────────────────────────────────
-office_js <- JS("
-function(data, type) {
-if (type !== 'display') return data;
-var cc = {Argentina:'ar', Edinburgh:'_Scotland', London:'_England',
-'New York':'us', 'San Francisco':'us', Sydney:'au', Tokyo:'jp'};
-var flag = cc[data] || '';
-return ' ' + data;
-}
-")
-salary_js <- JS("
-(function() {
-var nfmt = DataTable.render.number('.', ',', 2, 'R$ ');
-return function(data, type) {
-var txt = nfmt.display(data);
-if (type !== 'display') return txt;
-var c = data < 250000 ? 'red' : data < 500000 ? 'orange' : 'green';
-return '' + txt + '';
-};
-})()
-")
-extn_js <- JS("
-function(data, type) {
-return type === 'display'
-? ''
-: data;
-}
-")
-# ── Options ───────────────────────────────────────────────
-opts <- list(
-pageLength = 10,
-lengthMenu = c(10, 25, -1),
-columns = names(df),
-layout = list(
-topStart = "pageLength",
-topEnd = list(
-buttons = list(
-list(extend = "copyHtml5", text = "Copiar"),
-list(extend = "csvHtml5"),
-list(extend = "excelHtml5"),
-list(extend = "spacer", style = "bar"),
-list(extend = "colvis", text = "Colunas")
-),
-search = list(placeholder = "")
-),
-bottomEnd = "paging"
-),
-columnControl = list(
-target = 0,
-content = list("order", "searchDropdown", list(
-list(extend = "orderAsc", text = "Ordem crescente"),
-list(extend = "orderDesc", text = "Ordem decrescente"),
-"spacer",
-list(extend = "colVisDropdown", text = "Selecionar colunas")
-))
-),
-ordering = list(indicators = FALSE, handler = FALSE),
-columnDefs = list(
-list(targets = which(names(df) == "office") - 1L,
-className = "f32", render = office_js),
-list(targets = which(names(df) == "salary") - 1L,
-className = "dt-body-right", render = salary_js),
-list(targets = which(names(df) == "extn") - 1L,
-render = extn_js)
-),
-language = list(
-lengthMenu = "Mostrar _MENU_",
-search = "Buscar",
-info = "Mostrando _START_ a _END_ de _TOTAL_ registros",
-infoEmpty = "Nenhum registro",
-zeroRecords = "Nenhum registro encontrado",
-emptyTable = "Nenhum dado disponível",
-decimal = ",", thousands = ".", infoThousands = ".",
-lengthLabels = list(`10` = "10", `25` = "25", `-1` = "Todas"),
-paginate = list(first="«", previous="‹", `next`="›", last="»"),
-buttons = list(
-copyTitle = "Copiado!",
-copySuccess = list(`_` = "%d linhas copiadas", `1` = "1 linha copiada")
-),
-columnControl = list(
-orderAsc = "Crescente", orderDesc = "Decrescente",
-searchDropdown = "Pesquisar", colVisDropdown = "Colunas",
-searchClear = "Limpar",
-search = list(
-text = list(contains="Contém", starts="Começa por",
-ends="Termina em", equal="Igual a"),
-number = list(greater="Maior que", less="Menor que",
-equal="Igual a")
-)
-)
-)
-)
-dt2(df,
-compact = TRUE, striped = TRUE, hover = TRUE,
-font_scale = 0.85, responsive = FALSE,
-options = opts)
-})
-}
-shinyApp(ui, server)
-library(shiny)
-library(bslib)
-library(DT2)
-ui <- page_fillable(
-theme = bs_theme(version = 5, bootswatch = "flatly"),
-padding = "1rem",
-layout_columns(
-col_widths = c(6, 6),
-card(
-card_header("simple (prev / next only)"),
-card_body(dt2_output("tbl_simple"))
-),
-card(
-card_header("full_numbers (first / 1 2 3 / last)"),
-card_body(dt2_output("tbl_full"))
-)
-),
-card(
-card_header("No pagination"),
-card_body(dt2_output("tbl_none"))
-)
-)
-server <- function(input, output, session) {
-output$tbl_simple <- render_dt2({
-dt2(iris, options = list(pageLength = 5, pagingType = "simple"))
-})
-output$tbl_full <- render_dt2({
-dt2(iris, options = list(pageLength = 5, pagingType = "full_numbers"))
-})
-output$tbl_none <- render_dt2({
-dt2(iris[1:15, ], options = list(paging = FALSE))
-})
-}
-shinyApp(ui, server)
-pkgdown::build_site()
-pkgdown::build_site()
-pkgdown::build_site()
-pkgdown::build_site_github_pages()
-pkgdown::build_site_github_pages()
-pkgdown::build_site_github_pages()
-pkgdown::build_site_github_pages()
-pkgdown::build_site_github_pages()
-pkgdown::build_site_github_pages()
-usethis::use_cran_comments()
-pkgdown::build_site_github_pages()
-lang_ptbr <- list(
-# ── Core table ──────────────────────────────────────────────
-lengthMenu = "Mostrar _MENU_",
-search = "Buscar",
-info = "Mostrando _START_ a _END_ de _TOTAL_ registros",
-infoEmpty = "Mostrando 0 a 0 de 0 registros",
-infoFiltered = "(filtrado de _MAX_ registros no total)",
-zeroRecords = "Nenhum registro encontrado",
-emptyTable = "Nenhum dado disponível",
-loadingRecords = "Carregando dados...",
-decimal = ",",
-thousands = ".",
-infoThousands = ".",
-paginate = list(
-first = "«", previous = "‹", `next` = "›", last = "»"
-),
-# Rótulos do menu "entries per page"
-# (mapeia o valor numérico → texto exibido no select)
-lengthLabels = list(
-`10` = "10", `25` = "25", `50` = "50", `-1` = "Todas"
-),
-# ── Buttons extension ──────────────────────────────────────
-buttons = list(
-copyTitle = "Copiado para a área de transferência",
-copyKeys = paste0(
-"Pressione Ctrl ou \u2318 + C para copiar. ",
-"Pressione Esc para cancelar."
-),
-copySuccess = list(`_` = "%d linhas copiadas", `1` = "1 linha copiada")
-),
-# ── ColumnControl extension ────────────────────────────────
-columnControl = list(
-colVis = "Visibilidade da coluna",
-colVisDropdown = "Visibilidade da coluna",
-dropdown = "Mostrar mais...",
-orderAsc = "Ordem crescente",
-orderDesc = "Ordem decrescente",
-orderClear = "Remover ordenação",
-orderRemove = "Remover ordenação",
-searchClear = "Limpar pesquisa",
-searchDropdown = "Pesquisar",
-reorder = "Reordenar",
-reorderLeft = "Mover para a esquerda",
-reorderRight = "Mover para a direita",
-list = list(
-all = "Todos", empty = "Vazio", none = "Nenhum",
-search = "Pesquisar..."
-),
-search = list(
-text = list(
-contains = "Contém", empty = "Vazio", ends = "Termina em",
-equal = "Igual a", notContains = "Não contém",
-notEmpty = "Não está vazio", notEqual = "Diferente de",
-starts = "Começa por"
-),
-number = list(
-empty = "Vazio", equal = "Igual a",
-greater = "Maior que", greaterOrEqual = "Maior ou igual a",
-less = "Menor que", lessOrEqual = "Menor ou igual a",
-notEmpty = "Não está vazio", notEqual = "Diferente de"
-),
-datetime = list(
-empty = "Vazio", equal = "Igual a",
-greater = "Posterior a", less = "Anterior a",
-notEmpty = "Não está vazio", notEqual = "Diferente de"
-)
-)
-)
-)
-# Use it
-dt2(iris, options = list(pageLength = 10, language = lang_ptbr))
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..8e41a46
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,76 @@
+# CLAUDE.md
+
+Guidance for working in the **DT2** repository.
+
+## What this package is
+
+DT2 is an `htmlwidgets` binding for **DataTables v2** (the JS library by SpryMedia).
+It configures DataTables via plain R lists (1:1 with the JS API), with modular
+extension loading, Bootstrap 5 styling, and Shiny integration (proxy, events,
+inline inputs, server-side processing). Written from scratch for the v2 API —
+not a fork of the `DT` package.
+
+- R code: `R/` · JS runtime: `inst/htmlwidgets/dt2.js` · bundled libs: `inst/htmlwidgets/lib/`
+- Vignettes: `vignettes/` · built site: `docs/` (gitignored, see below)
+
+## Common commands
+
+```r
+# load for interactive dev / run a quick check
+pkgload::load_all(".")
+
+# regenerate Rd from roxygen (package uses roxygen2 8 — Config/roxygen2/version)
+roxygen2::roxygenise()
+
+# tests (testthat edition 3)
+testthat::test_dir("tests/testthat")
+
+# full check (clean: 0/0/0)
+devtools::check(args = "--as-cran")
+
+# CRAN pre-submission (results emailed to maintainer)
+devtools::check_win_devel(); devtools::check_win_release()
+```
+
+## Conventions / gotchas
+
+- **roxygen2 8**: version is recorded in `Config/roxygen2/version` (no `RoxygenNote`).
+ Run `roxygenise()` after changing any `#'` block. Build with the system R
+ (`Rscript`/`R CMD`), which is R 4.6 here.
+- **`options$columns` is the name→index map.** Name-based helpers
+ (`dt2_cols_*`, `dt2_format_*`, `dt2_order`, ...) resolve names against
+ `options$columns`. The user must set `opts <- list(columns = names(df))`
+ *before* calling them; `dt2()` injects it from the data, but that runs after
+ the helpers. Resolution is centralized in `.dt2_name_to_idx()` (`R/dt2_utils.R`),
+ which **warns** on unresolved names instead of returning silent `NA`.
+- **Generated JS must be quote-safe.** Use `.dt2_js_str()` (jsonlite) to build
+ JS string literals, never `sprintf("'%s'", x)`.
+- **SSP query strings:** `dt2.js` encodes query-string *keys* with
+ `encodeURIComponent`, so the server parser (`.dt2_parse_ssp_request`) must
+ URL-decode keys, not just values.
+- **CRAN is live:** DT2 is on CRAN. Public API is frozen — do not remove
+ exported functions; cross-link/deprecate instead.
+
+## pkgdown site (`docs/`)
+
+- The site theme uses **`tidytemplate`** (NOT on CRAN). Install before building:
+ `pak::pak("tidyverse/tidytemplate")`.
+- `docs/` is in `.gitignore` but the tracked site is **force-committed**. To
+ rebuild and commit:
+ 1. `R CMD INSTALL .` then `pkgdown::build_site(install = FALSE)`
+ 2. delete orphaned versioned asset dirs (old `dt2-binding-*`, old `bootstrap-*`)
+ 3. `git add -f docs/`, then unstage every `.DS_Store`
+- `_pkgdown.yml` has a curated grouped `reference:` index — keep it in sync with
+ `NAMESPACE` exports (every export must appear exactly once or pkgdown errors).
+
+## Release flow (followed for 0.1.2)
+
+Bump `DESCRIPTION` Version+Date → update `NEWS.md` → update `cran-comments.md`
+→ `check(--as-cran)` + win-builder devel/release → rebuild site → publish GitHub
+release/tag `vX.Y.Z` (`gh release ...`) → `devtools::submit_cran()` (maintainer
+runs this; CRAN confirmation is emailed).
+
+## House rules in this repo
+
+- Never stage `.DS_Store` or `.Rhistory` (they show as modified but are noise).
+- Branch per change + PR + merge; reference issues with `Closes #N`.
diff --git a/CRAN-SUBMISSION b/CRAN-SUBMISSION
index 4cc83fa..f5454d8 100644
--- a/CRAN-SUBMISSION
+++ b/CRAN-SUBMISSION
@@ -1,3 +1,3 @@
-Version: 0.1.1
-Date: 2026-04-29 17:40:56 UTC
-SHA: da1c22281b59b3c8d60f33abb4cc1de8a5284390
+Version: 0.1.2
+Date: 2026-06-13 23:44:56 UTC
+SHA: ee47dc3fb6b625f3416a23e6c9e0a302f4591fb2
diff --git a/docs/.DS_Store b/docs/.DS_Store
deleted file mode 100644
index dde5254..0000000
Binary files a/docs/.DS_Store and /dev/null differ
diff --git a/docs/deps/.DS_Store b/docs/deps/.DS_Store
deleted file mode 100644
index 01ac6d5..0000000
Binary files a/docs/deps/.DS_Store and /dev/null differ
diff --git a/docs/reference/.DS_Store b/docs/reference/.DS_Store
deleted file mode 100644
index 3a57019..0000000
Binary files a/docs/reference/.DS_Store and /dev/null differ
diff --git a/inst/.DS_Store b/inst/.DS_Store
deleted file mode 100644
index 07b2b96..0000000
Binary files a/inst/.DS_Store and /dev/null differ