# Pbir Bookmarks

> Use when creating, editing, or wiring Power BI PBIR bookmarks in Report.Report/definition/bookmarks/ — bookmarks.json index and individual bookmark.json files. Covers bookmark structure (name hex ID matching items[], displayName, options, explorationState), options flags (targetVisualNames, suppressDisplay, suppressActiveSection, suppressData, applyOnlyToTargetVisuals), explorationState shape (ac…

- **Type:** Skill
- **Install:** `agentstack add skill-wardawgmalvicious-claude-config-pbir-bookmarks`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [wardawgmalvicious](https://agentstack.voostack.com/s/wardawgmalvicious)
- **Installs:** 0
- **Category:** [AI & ML](https://agentstack.voostack.com/c/ai-and-ml)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [wardawgmalvicious](https://github.com/wardawgmalvicious)
- **Source:** https://github.com/wardawgmalvicious/claude-config/tree/main/skills/pbir-bookmarks

## Install

```sh
agentstack add skill-wardawgmalvicious-claude-config-pbir-bookmarks
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

## PBIR Bookmarks Reference

Bookmarks capture a snapshot of report state — active page, filter selections, visual visibility, and object overrides. Used for toggle interactivity via button `visualLink` actions.

### File Layout

```
Report.Report/definition/bookmarks/
  bookmarks.json                       # index + display order
  .bookmark.json               # one per bookmark
```

### bookmarks.json

```json
{
  "$schema": ".../bookmarksMetadata/1.0.0/schema.json",
  "items": [
    {"name": "958f29ad733c047ee0b8"},
    {"name": "54698b9cd0a0c57906b7"}
  ]
}
```

Order in `items[]` = order in the Bookmarks pane.

### [id].bookmark.json Top-Level

| Property | Type | Notes |
|---|---|---|
| `$schema` | string | `.../bookmark/1.4.0/schema.json` |
| `name` | string | 20-char hex; must match entry in `bookmarks.json` |
| `displayName` | string | Label shown in Bookmarks pane |
| `options` | object | Apply behavior flags |
| `explorationState` | object | The actual state snapshot |

### options

| Property | Type | Notes |
|---|---|---|
| `targetVisualNames` | string[] | Visuals affected by this bookmark (empty/omitted = all) |
| `suppressDisplay` | boolean | `true` = don't change visibility when applied; preserves current show/hide state |
| `suppressActiveSection` | boolean | Don't change the active page when applied |
| `suppressData` | boolean | Don't restore filter / slicer state |
| `applyOnlyToTargetVisuals` | boolean | Only touch visuals in `targetVisualNames` |

### explorationState

```json
"explorationState": {
  "version": "1.3",
  "activeSection": "",
  "filters": {"byExpr": [...]},
  "sections": {
    "": {
      "visualContainers": {
        "": {
          "singleVisual": {
            "display": {"mode": "hidden"},
            "objects": {"merge": {...}},
            "activeProjections": {...}
          },
          "filters": {"byExpr": [...]}
        }
      }
    }
  },
  "objects": {"merge": {"outspacePane": [{"properties": {...}}]}}
}
```

| Path | Description |
|---|---|
| `version` | Bookmark schema version (e.g. `"1.3"`) |
| `activeSection` | Page ID active when bookmark applied |
| `filters.byExpr[]` | Report-level filter state snapshot |
| `sections.` | Per-page overrides |
| `sections..visualContainers.` | Per-visual state |
| `objects.merge` | Report-level UI state (filter pane visible/expanded, etc.) |

### Per-Visual State (singleVisual)

| Path | Notes |
|---|---|
| `display.mode` | `"hidden"` or `"visible"` — show/hide mechanism |
| `objects.merge` | Merge these formatting properties over base visual.json |
| `activeProjections` | Active drill-down field selection |
| `filters.byExpr[]` | Visual-level filter snapshot |

Hiding / showing a visual via bookmark is driven by `display.mode`, NOT by changing the visual's root `isHidden`.

### byExpr Filter Entry

```json
{
  "name": "d3f20cea05c37b47123a",
  "type": "Categorical",
  "expression": {
    "Column": {
      "Expression": {"SourceRef": {"Entity": "Date"}},
      "Property": "Calendar Month (ie Jan)"
    }
  },
  "filter": {
    "Version": 2,
    "From": [{"Name": "e", "Entity": "Date", "Type": 0}],
    "Where": [{"Condition": {"In": {
      "Expressions": [{"Column": {"Expression": {"SourceRef": {"Source": "e"}}, "Property": "Calendar Month (ie Jan)"}}],
      "Values": [[{"Literal": {"Value": "'Jan'"}}]]
    }}}]
  },
  "howCreated": 1
}
```

| Field | Notes |
|---|---|
| `name` | Matches the `name` of the filter in `filterConfig` |
| `type` | Same types as filters: `Categorical`, `Advanced`, `TopN`, `RelativeDate`, etc. |
| `expression` | **Required** — the field the bookmark tracks |
| `filter` | **Optional** — omit when the field is tracked but has no active selection |
| `howCreated` | `0` = visual-level, `1` = report-level |

Same `SourceRef.Source` alias rule as regular filters: `Where` conditions reference the alias from `From[]`, not `Entity`.

### Common Patterns

| Pattern | How |
|---|---|
| Toggle visual visibility | Two bookmarks with opposite `singleVisual.display.mode` values; wire to buttons via `visualLink` actions |
| Reset all filters | Empty `byExpr[]` entries, or `filter.Where: []` per field |
| Guided navigation | Chain bookmarks, each setting `activeSection` + pre-set filters |
| Freeze visibility on filter-only bookmark | `options.suppressDisplay: true` |
| Freeze filter state on visibility-only bookmark | `options.suppressData: true` |
| Scope to subset of visuals | `options.targetVisualNames` + `applyOnlyToTargetVisuals: true` |

### objects.merge Example

Override title text on a single visual in a bookmark:

```json
"singleVisual": {
  "display": {"mode": "visible"},
  "objects": {"merge": {
    "title": [{"properties": {
      "text": {"expr": {"Literal": {"Value": "'Quarterly View'"}}}
    }}]
  }}
}
```

Report-level — collapse the filter pane when applied:

```json
"objects": {"merge": {
  "outspacePane": [{"properties": {
    "expanded": {"expr": {"Literal": {"Value": "false"}}}
  }}]
}}
```

### Wiring to a Button

Buttons apply a bookmark via a `visualLink` action type on the button's `onClick` in its visual.json. The action payload references the bookmark's `name` (the hex ID).

### Gotchas

| Issue | Cause | Fix |
|---|---|---|
| Bookmark not listed in pane | Missing entry in `bookmarks.json.items[]` | Add `{"name": ""}` — order here controls pane order |
| Bookmark applies but nothing changes | `suppressDisplay` and `suppressData` both true | Loosen the flags |
| Visibility toggle has no effect | Used root-level `isHidden` instead of `singleVisual.display.mode` | Use `display.mode: "hidden"` / `"visible"` in `explorationState` |
| Bookmark filter ignored | Missing `expression` on byExpr entry | `expression` is required even when `filter` is omitted |
| Filter state jitters on toggle | Inconsistent field set between bookmarks | Include the same `byExpr` entries in every bookmark that participates in the toggle |
| Wrong page opens | `activeSection` out of sync with page rename | Update `activeSection` to the new page hex ID |
| SourceRef used Entity in Where | Same gotcha as filters | Use `{"Source": ""}` |
| Button doesn't fire bookmark | Wrong bookmark name in action payload | Must match hex `name`, not `displayName` |
| Partial visual update | `targetVisualNames` includes wrong IDs | Verify visual `name` values — these are the root-level IDs, not displayNames |
| `howCreated` wrong scope | `0` vs `1` mixed up | `0` = visual-level filter, `1` = report-level filter |

### Reference

- Microsoft Learn: [Create report bookmarks in Power BI](https://learn.microsoft.com/power-bi/create-reports/desktop-bookmarks)
- Microsoft Learn: [Power BI Desktop project — report folder layout](https://learn.microsoft.com/power-bi/developer/projects/projects-report)
- Microsoft Learn: [Create and configure buttons (Bookmark action)](https://learn.microsoft.com/power-bi/create-reports/desktop-buttons)
- Comprehensive MS Learn link bundle (user concept / button wiring / PBIR file format / REST round-trip): [references/REFERENCE.md](references/REFERENCE.md)

### See also

- `pbir-visual-json` — visual `name` IDs targeted by bookmarks and button actions
- `pbir-filters` — `byExpr` uses the same SQExpr / SourceRef.Source shape

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [wardawgmalvicious](https://github.com/wardawgmalvicious)
- **Source:** [wardawgmalvicious/claude-config](https://github.com/wardawgmalvicious/claude-config)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-wardawgmalvicious-claude-config-pbir-bookmarks
- Seller: https://agentstack.voostack.com/s/wardawgmalvicious
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
