ActivityTimeline has 18 more hardcoded English literals — the empty state was one of a set, and 12 of them already have pack keys sitting unused #7149

Description

@os-warren

Split out of #7142, which fixed one literal in packages/plugin-detail/src/ActivityTimeline.tsx (the empty-state title). Sweeping the file while implementing that fix found the empty state was not a lone oversight: the component uses no translation hook at all on origin/main — it imported nothing from @object-ui/i18n and made no t() call anywhere. Filed rather than fixed, because widening #7142's scope was the PM's call, not the implementing agent's.

Measured

Rendered ActivityTimeline with activities={[]} under a zh I18nProvider, and its sibling RecordActivityTimeline the same way, reading textContent off both containers:

[zh] ActivityTimeline => "Activity(0)No activity recorded"
[zh] RecordActivityTimeline => "活动(0)全部动态暂无活动记录"
[no-provider, filterable] ActivityTimeline => "Activity(0)AllField ChangesCreatesDeletesCommentsStatus ChangesNo activity recorded"

The sibling renders the same surface entirely in Chinese. After #7142 the zh card reads "Activity(0)暂无活动记录" — correct for the empty state, still English for the card title beside it.

Inventory

Line numbers are on the post-#7142 file. "pack key" means a key whose value in the en pack is already the exact literal, read out of the pack objects directly (not grepped — the packs are nested, so a dotted-key grep returns zero for keys that exist).

Relative timestamps, formatTimestamp:

lineliteralexisting key
49'just now'detail.justNow
50`${diffMins}m ago`detail.minutesAgo = '{{count}}m ago'
52`${diffHours}h ago`detail.hoursAgo
54`${diffDays}d ago`detail.daysAgo

Filter chips, FILTER_LABELS:

lineliteralexisting key
82'All'none
83'Field Changes'detail.fieldChangesFilter
84'Creates'none
85'Deletes'none
86'Comments'detail.comments
87'Status Changes'none

Card chrome:

lineliteralexisting key
109Activity (the CardTitle text node)detail.activity
118aria-label="Activity type filter"none (detail.filterActivity = 'Filter activity' is close but not identical)

Activity descriptions, formatFieldChange — sentences assembled in code, so these need new keys with interpolation holes rather than a lookup:

lineliteralexisting key
66, 67, 74'(empty)'none
68`Changed ${fieldLabel} from "${oldVal}" to "${newVal}"`none
71'Created this record'none
72'Deleted this record'none
75`Changed status to "${newVal}"`none
78'Updated record'none

So 12 of the 18 are a lookup swap against keys that already exist and are already translated in all ten packs; the remaining 6 (plus the two chip labels and the aria label) need new keys across en.ts and its nine siblings, with all-locales-key-parity green. Never an inline defaultValue (#3517).

Reachability caveat, worth checking before sizing this

DetailView gates the timeline on hasActivity = !!schema.activities && schema.activities.length > 0 (DetailView.tsx:1408) and never passes filterable, so from that host the filter chips never render and the empty state is unreachable. The chips and the filtered-empty path are reachable through the published export (packages/plugin-detail/src/index.tsx:118) by a consumer passing filterable. That may change how this is prioritized, but it does not change the timestamps or the card title, which render on every activity tab.

Unrelated observation in the same file

Filter is imported from lucide-react at line 11 and never used — eslint reports it as a warning today (@typescript-eslint/no-unused-vars, warning not error, so nothing is red).

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
     blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    
    Skip to content

    ActivityTimeline has 18 more hardcoded English literals — the empty state was one of a set, and 12 of them already have pack keys sitting unused #7149

    Description

    @os-warren

    Split out of #7142, which fixed one literal in packages/plugin-detail/src/ActivityTimeline.tsx (the empty-state title). Sweeping the file while implementing that fix found the empty state was not a lone oversight: the component uses no translation hook at all on origin/main — it imported nothing from @object-ui/i18n and made no t() call anywhere. Filed rather than fixed, because widening #7142's scope was the PM's call, not the implementing agent's.

    Measured

    Rendered ActivityTimeline with activities={[]} under a zh I18nProvider, and its sibling RecordActivityTimeline the same way, reading textContent off both containers:

    [zh] ActivityTimeline => "Activity(0)No activity recorded"
    [zh] RecordActivityTimeline => "活动(0)全部动态暂无活动记录"
    [no-provider, filterable] ActivityTimeline => "Activity(0)AllField ChangesCreatesDeletesCommentsStatus ChangesNo activity recorded"
    

    The sibling renders the same surface entirely in Chinese. After #7142 the zh card reads "Activity(0)暂无活动记录" — correct for the empty state, still English for the card title beside it.

    Inventory

    Line numbers are on the post-#7142 file. "pack key" means a key whose value in the en pack is already the exact literal, read out of the pack objects directly (not grepped — the packs are nested, so a dotted-key grep returns zero for keys that exist).

    Relative timestamps, formatTimestamp:

    lineliteralexisting key
    49'just now'detail.justNow
    50`${diffMins}m ago`detail.minutesAgo = '{{count}}m ago'
    52`${diffHours}h ago`detail.hoursAgo
    54`${diffDays}d ago`detail.daysAgo

    Filter chips, FILTER_LABELS:

    lineliteralexisting key
    82'All'none
    83'Field Changes'detail.fieldChangesFilter
    84'Creates'none
    85'Deletes'none
    86'Comments'detail.comments
    87'Status Changes'none

    Card chrome:

    lineliteralexisting key
    109Activity (the CardTitle text node)detail.activity
    118aria-label="Activity type filter"none (detail.filterActivity = 'Filter activity' is close but not identical)

    Activity descriptions, formatFieldChange — sentences assembled in code, so these need new keys with interpolation holes rather than a lookup:

    lineliteralexisting key
    66, 67, 74'(empty)'none
    68`Changed ${fieldLabel} from "${oldVal}" to "${newVal}"`none
    71'Created this record'none
    72'Deleted this record'none
    75`Changed status to "${newVal}"`none
    78'Updated record'none

    So 12 of the 18 are a lookup swap against keys that already exist and are already translated in all ten packs; the remaining 6 (plus the two chip labels and the aria label) need new keys across en.ts and its nine siblings, with all-locales-key-parity green. Never an inline defaultValue (#3517).

    Reachability caveat, worth checking before sizing this

    DetailView gates the timeline on hasActivity = !!schema.activities && schema.activities.length > 0 (DetailView.tsx:1408) and never passes filterable, so from that host the filter chips never render and the empty state is unreachable. The chips and the filtered-empty path are reachable through the published export (packages/plugin-detail/src/index.tsx:118) by a consumer passing filterable. That may change how this is prioritized, but it does not change the timestamps or the card title, which render on every activity tab.

    Unrelated observation in the same file

    Filter is imported from lucide-react at line 11 and never used — eslint reports it as a warning today (@typescript-eslint/no-unused-vars, warning not error, so nothing is red).

    Metadata

    Metadata

    Assignees

    Labels

    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
      Skip to content

      ActivityTimeline has 18 more hardcoded English literals — the empty state was one of a set, and 12 of them already have pack keys sitting unused #7149

      Description

      @os-warren

      Split out of #7142, which fixed one literal in packages/plugin-detail/src/ActivityTimeline.tsx (the empty-state title). Sweeping the file while implementing that fix found the empty state was not a lone oversight: the component uses no translation hook at all on origin/main — it imported nothing from @object-ui/i18n and made no t() call anywhere. Filed rather than fixed, because widening #7142's scope was the PM's call, not the implementing agent's.

      Measured

      Rendered ActivityTimeline with activities={[]} under a zh I18nProvider, and its sibling RecordActivityTimeline the same way, reading textContent off both containers:

      [zh] ActivityTimeline => "Activity(0)No activity recorded"
      [zh] RecordActivityTimeline => "活动(0)全部动态暂无活动记录"
      [no-provider, filterable] ActivityTimeline => "Activity(0)AllField ChangesCreatesDeletesCommentsStatus ChangesNo activity recorded"
      

      The sibling renders the same surface entirely in Chinese. After #7142 the zh card reads "Activity(0)暂无活动记录" — correct for the empty state, still English for the card title beside it.

      Inventory

      Line numbers are on the post-#7142 file. "pack key" means a key whose value in the en pack is already the exact literal, read out of the pack objects directly (not grepped — the packs are nested, so a dotted-key grep returns zero for keys that exist).

      Relative timestamps, formatTimestamp:

      lineliteralexisting key
      49'just now'detail.justNow
      50`${diffMins}m ago`detail.minutesAgo = '{{count}}m ago'
      52`${diffHours}h ago`detail.hoursAgo
      54`${diffDays}d ago`detail.daysAgo

      Filter chips, FILTER_LABELS:

      lineliteralexisting key
      82'All'none
      83'Field Changes'detail.fieldChangesFilter
      84'Creates'none
      85'Deletes'none
      86'Comments'detail.comments
      87'Status Changes'none

      Card chrome:

      lineliteralexisting key
      109Activity (the CardTitle text node)detail.activity
      118aria-label="Activity type filter"none (detail.filterActivity = 'Filter activity' is close but not identical)

      Activity descriptions, formatFieldChange — sentences assembled in code, so these need new keys with interpolation holes rather than a lookup:

      lineliteralexisting key
      66, 67, 74'(empty)'none
      68`Changed ${fieldLabel} from "${oldVal}" to "${newVal}"`none
      71'Created this record'none
      72'Deleted this record'none
      75`Changed status to "${newVal}"`none
      78'Updated record'none

      So 12 of the 18 are a lookup swap against keys that already exist and are already translated in all ten packs; the remaining 6 (plus the two chip labels and the aria label) need new keys across en.ts and its nine siblings, with all-locales-key-parity green. Never an inline defaultValue (#3517).

      Reachability caveat, worth checking before sizing this

      DetailView gates the timeline on hasActivity = !!schema.activities && schema.activities.length > 0 (DetailView.tsx:1408) and never passes filterable, so from that host the filter chips never render and the empty state is unreachable. The chips and the filtered-empty path are reachable through the published export (packages/plugin-detail/src/index.tsx:118) by a consumer passing filterable. That may change how this is prioritized, but it does not change the timestamps or the card title, which render on every activity tab.

      Unrelated observation in the same file

      Filter is imported from lucide-react at line 11 and never used — eslint reports it as a warning today (@typescript-eslint/no-unused-vars, warning not error, so nothing is red).

      Metadata

      Metadata

      Assignees

      Labels

      domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

      Type

      No type

      Projects

      No projects

        Milestone

        No milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
        Skip to content

        ActivityTimeline has 18 more hardcoded English literals — the empty state was one of a set, and 12 of them already have pack keys sitting unused #7149

        Description

        @os-warren

        Split out of #7142, which fixed one literal in packages/plugin-detail/src/ActivityTimeline.tsx (the empty-state title). Sweeping the file while implementing that fix found the empty state was not a lone oversight: the component uses no translation hook at all on origin/main — it imported nothing from @object-ui/i18n and made no t() call anywhere. Filed rather than fixed, because widening #7142's scope was the PM's call, not the implementing agent's.

        Measured

        Rendered ActivityTimeline with activities={[]} under a zh I18nProvider, and its sibling RecordActivityTimeline the same way, reading textContent off both containers:

        [zh] ActivityTimeline => "Activity(0)No activity recorded"
        [zh] RecordActivityTimeline => "活动(0)全部动态暂无活动记录"
        [no-provider, filterable] ActivityTimeline => "Activity(0)AllField ChangesCreatesDeletesCommentsStatus ChangesNo activity recorded"
        

        The sibling renders the same surface entirely in Chinese. After #7142 the zh card reads "Activity(0)暂无活动记录" — correct for the empty state, still English for the card title beside it.

        Inventory

        Line numbers are on the post-#7142 file. "pack key" means a key whose value in the en pack is already the exact literal, read out of the pack objects directly (not grepped — the packs are nested, so a dotted-key grep returns zero for keys that exist).

        Relative timestamps, formatTimestamp:

        lineliteralexisting key
        49'just now'detail.justNow
        50`${diffMins}m ago`detail.minutesAgo = '{{count}}m ago'
        52`${diffHours}h ago`detail.hoursAgo
        54`${diffDays}d ago`detail.daysAgo

        Filter chips, FILTER_LABELS:

        lineliteralexisting key
        82'All'none
        83'Field Changes'detail.fieldChangesFilter
        84'Creates'none
        85'Deletes'none
        86'Comments'detail.comments
        87'Status Changes'none

        Card chrome:

        lineliteralexisting key
        109Activity (the CardTitle text node)detail.activity
        118aria-label="Activity type filter"none (detail.filterActivity = 'Filter activity' is close but not identical)

        Activity descriptions, formatFieldChange — sentences assembled in code, so these need new keys with interpolation holes rather than a lookup:

        lineliteralexisting key
        66, 67, 74'(empty)'none
        68`Changed ${fieldLabel} from "${oldVal}" to "${newVal}"`none
        71'Created this record'none
        72'Deleted this record'none
        75`Changed status to "${newVal}"`none
        78'Updated record'none

        So 12 of the 18 are a lookup swap against keys that already exist and are already translated in all ten packs; the remaining 6 (plus the two chip labels and the aria label) need new keys across en.ts and its nine siblings, with all-locales-key-parity green. Never an inline defaultValue (#3517).

        Reachability caveat, worth checking before sizing this

        DetailView gates the timeline on hasActivity = !!schema.activities && schema.activities.length > 0 (DetailView.tsx:1408) and never passes filterable, so from that host the filter chips never render and the empty state is unreachable. The chips and the filtered-empty path are reachable through the published export (packages/plugin-detail/src/index.tsx:118) by a consumer passing filterable. That may change how this is prioritized, but it does not change the timestamps or the card title, which render on every activity tab.

        Unrelated observation in the same file

        Filter is imported from lucide-react at line 11 and never used — eslint reports it as a warning today (@typescript-eslint/no-unused-vars, warning not error, so nothing is red).

        Metadata

        Metadata

        Assignees

        Labels

        domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
          Skip to content

          ActivityTimeline has 18 more hardcoded English literals — the empty state was one of a set, and 12 of them already have pack keys sitting unused #7149

          Description

          @os-warren

          Split out of #7142, which fixed one literal in packages/plugin-detail/src/ActivityTimeline.tsx (the empty-state title). Sweeping the file while implementing that fix found the empty state was not a lone oversight: the component uses no translation hook at all on origin/main — it imported nothing from @object-ui/i18n and made no t() call anywhere. Filed rather than fixed, because widening #7142's scope was the PM's call, not the implementing agent's.

          Measured

          Rendered ActivityTimeline with activities={[]} under a zh I18nProvider, and its sibling RecordActivityTimeline the same way, reading textContent off both containers:

          [zh] ActivityTimeline => "Activity(0)No activity recorded"
          [zh] RecordActivityTimeline => "活动(0)全部动态暂无活动记录"
          [no-provider, filterable] ActivityTimeline => "Activity(0)AllField ChangesCreatesDeletesCommentsStatus ChangesNo activity recorded"
          

          The sibling renders the same surface entirely in Chinese. After #7142 the zh card reads "Activity(0)暂无活动记录" — correct for the empty state, still English for the card title beside it.

          Inventory

          Line numbers are on the post-#7142 file. "pack key" means a key whose value in the en pack is already the exact literal, read out of the pack objects directly (not grepped — the packs are nested, so a dotted-key grep returns zero for keys that exist).

          Relative timestamps, formatTimestamp:

          lineliteralexisting key
          49'just now'detail.justNow
          50`${diffMins}m ago`detail.minutesAgo = '{{count}}m ago'
          52`${diffHours}h ago`detail.hoursAgo
          54`${diffDays}d ago`detail.daysAgo

          Filter chips, FILTER_LABELS:

          lineliteralexisting key
          82'All'none
          83'Field Changes'detail.fieldChangesFilter
          84'Creates'none
          85'Deletes'none
          86'Comments'detail.comments
          87'Status Changes'none

          Card chrome:

          lineliteralexisting key
          109Activity (the CardTitle text node)detail.activity
          118aria-label="Activity type filter"none (detail.filterActivity = 'Filter activity' is close but not identical)

          Activity descriptions, formatFieldChange — sentences assembled in code, so these need new keys with interpolation holes rather than a lookup:

          lineliteralexisting key
          66, 67, 74'(empty)'none
          68`Changed ${fieldLabel} from "${oldVal}" to "${newVal}"`none
          71'Created this record'none
          72'Deleted this record'none
          75`Changed status to "${newVal}"`none
          78'Updated record'none

          So 12 of the 18 are a lookup swap against keys that already exist and are already translated in all ten packs; the remaining 6 (plus the two chip labels and the aria label) need new keys across en.ts and its nine siblings, with all-locales-key-parity green. Never an inline defaultValue (#3517).

          Reachability caveat, worth checking before sizing this

          DetailView gates the timeline on hasActivity = !!schema.activities && schema.activities.length > 0 (DetailView.tsx:1408) and never passes filterable, so from that host the filter chips never render and the empty state is unreachable. The chips and the filtered-empty path are reachable through the published export (packages/plugin-detail/src/index.tsx:118) by a consumer passing filterable. That may change how this is prioritized, but it does not change the timestamps or the card title, which render on every activity tab.

          Unrelated observation in the same file

          Filter is imported from lucide-react at line 11 and never used — eslint reports it as a warning today (@typescript-eslint/no-unused-vars, warning not error, so nothing is red).

          Metadata

          Metadata

          Assignees

          Labels

          domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

          Type

          No type

          Projects

          No projects

            Milestone

            No milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
            Skip to content

            ActivityTimeline has 18 more hardcoded English literals — the empty state was one of a set, and 12 of them already have pack keys sitting unused #7149

            Description

            @os-warren

            Split out of #7142, which fixed one literal in packages/plugin-detail/src/ActivityTimeline.tsx (the empty-state title). Sweeping the file while implementing that fix found the empty state was not a lone oversight: the component uses no translation hook at all on origin/main — it imported nothing from @object-ui/i18n and made no t() call anywhere. Filed rather than fixed, because widening #7142's scope was the PM's call, not the implementing agent's.

            Measured

            Rendered ActivityTimeline with activities={[]} under a zh I18nProvider, and its sibling RecordActivityTimeline the same way, reading textContent off both containers:

            [zh] ActivityTimeline => "Activity(0)No activity recorded"
            [zh] RecordActivityTimeline => "活动(0)全部动态暂无活动记录"
            [no-provider, filterable] ActivityTimeline => "Activity(0)AllField ChangesCreatesDeletesCommentsStatus ChangesNo activity recorded"
            

            The sibling renders the same surface entirely in Chinese. After #7142 the zh card reads "Activity(0)暂无活动记录" — correct for the empty state, still English for the card title beside it.

            Inventory

            Line numbers are on the post-#7142 file. "pack key" means a key whose value in the en pack is already the exact literal, read out of the pack objects directly (not grepped — the packs are nested, so a dotted-key grep returns zero for keys that exist).

            Relative timestamps, formatTimestamp:

            lineliteralexisting key
            49'just now'detail.justNow
            50`${diffMins}m ago`detail.minutesAgo = '{{count}}m ago'
            52`${diffHours}h ago`detail.hoursAgo
            54`${diffDays}d ago`detail.daysAgo

            Filter chips, FILTER_LABELS:

            lineliteralexisting key
            82'All'none
            83'Field Changes'detail.fieldChangesFilter
            84'Creates'none
            85'Deletes'none
            86'Comments'detail.comments
            87'Status Changes'none

            Card chrome:

            lineliteralexisting key
            109Activity (the CardTitle text node)detail.activity
            118aria-label="Activity type filter"none (detail.filterActivity = 'Filter activity' is close but not identical)

            Activity descriptions, formatFieldChange — sentences assembled in code, so these need new keys with interpolation holes rather than a lookup:

            lineliteralexisting key
            66, 67, 74'(empty)'none
            68`Changed ${fieldLabel} from "${oldVal}" to "${newVal}"`none
            71'Created this record'none
            72'Deleted this record'none
            75`Changed status to "${newVal}"`none
            78'Updated record'none

            So 12 of the 18 are a lookup swap against keys that already exist and are already translated in all ten packs; the remaining 6 (plus the two chip labels and the aria label) need new keys across en.ts and its nine siblings, with all-locales-key-parity green. Never an inline defaultValue (#3517).

            Reachability caveat, worth checking before sizing this

            DetailView gates the timeline on hasActivity = !!schema.activities && schema.activities.length > 0 (DetailView.tsx:1408) and never passes filterable, so from that host the filter chips never render and the empty state is unreachable. The chips and the filtered-empty path are reachable through the published export (packages/plugin-detail/src/index.tsx:118) by a consumer passing filterable. That may change how this is prioritized, but it does not change the timestamps or the card title, which render on every activity tab.

            Unrelated observation in the same file

            Filter is imported from lucide-react at line 11 and never used — eslint reports it as a warning today (@typescript-eslint/no-unused-vars, warning not error, so nothing is red).

            Metadata

            Metadata

            Assignees

            Labels

            domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              ActivityTimeline has 18 more hardcoded English literals — the empty state was one of a set, and 12 of them already have pack keys sitting unused #7149

              Description

              @os-warren

              Split out of #7142, which fixed one literal in packages/plugin-detail/src/ActivityTimeline.tsx (the empty-state title). Sweeping the file while implementing that fix found the empty state was not a lone oversight: the component uses no translation hook at all on origin/main — it imported nothing from @object-ui/i18n and made no t() call anywhere. Filed rather than fixed, because widening #7142's scope was the PM's call, not the implementing agent's.

              Measured

              Rendered ActivityTimeline with activities={[]} under a zh I18nProvider, and its sibling RecordActivityTimeline the same way, reading textContent off both containers:

              [zh] ActivityTimeline => "Activity(0)No activity recorded"
              [zh] RecordActivityTimeline => "活动(0)全部动态暂无活动记录"
              [no-provider, filterable] ActivityTimeline => "Activity(0)AllField ChangesCreatesDeletesCommentsStatus ChangesNo activity recorded"
              

              The sibling renders the same surface entirely in Chinese. After #7142 the zh card reads "Activity(0)暂无活动记录" — correct for the empty state, still English for the card title beside it.

              Inventory

              Line numbers are on the post-#7142 file. "pack key" means a key whose value in the en pack is already the exact literal, read out of the pack objects directly (not grepped — the packs are nested, so a dotted-key grep returns zero for keys that exist).

              Relative timestamps, formatTimestamp:

              lineliteralexisting key
              49'just now'detail.justNow
              50`${diffMins}m ago`detail.minutesAgo = '{{count}}m ago'
              52`${diffHours}h ago`detail.hoursAgo
              54`${diffDays}d ago`detail.daysAgo

              Filter chips, FILTER_LABELS:

              lineliteralexisting key
              82'All'none
              83'Field Changes'detail.fieldChangesFilter
              84'Creates'none
              85'Deletes'none
              86'Comments'detail.comments
              87'Status Changes'none

              Card chrome:

              lineliteralexisting key
              109Activity (the CardTitle text node)detail.activity
              118aria-label="Activity type filter"none (detail.filterActivity = 'Filter activity' is close but not identical)

              Activity descriptions, formatFieldChange — sentences assembled in code, so these need new keys with interpolation holes rather than a lookup:

              lineliteralexisting key
              66, 67, 74'(empty)'none
              68`Changed ${fieldLabel} from "${oldVal}" to "${newVal}"`none
              71'Created this record'none
              72'Deleted this record'none
              75`Changed status to "${newVal}"`none
              78'Updated record'none

              So 12 of the 18 are a lookup swap against keys that already exist and are already translated in all ten packs; the remaining 6 (plus the two chip labels and the aria label) need new keys across en.ts and its nine siblings, with all-locales-key-parity green. Never an inline defaultValue (#3517).

              Reachability caveat, worth checking before sizing this

              DetailView gates the timeline on hasActivity = !!schema.activities && schema.activities.length > 0 (DetailView.tsx:1408) and never passes filterable, so from that host the filter chips never render and the empty state is unreachable. The chips and the filtered-empty path are reachable through the published export (packages/plugin-detail/src/index.tsx:118) by a consumer passing filterable. That may change how this is prioritized, but it does not change the timestamps or the card title, which render on every activity tab.

              Unrelated observation in the same file

              Filter is imported from lucide-react at line 11 and never used — eslint reports it as a warning today (@typescript-eslint/no-unused-vars, warning not error, so nothing is red).

              Metadata

              Metadata

              Assignees

              Labels

              domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

              Type

              No type

              Projects

              No projects

                Milestone

                No milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                Skip to content

                ActivityTimeline has 18 more hardcoded English literals — the empty state was one of a set, and 12 of them already have pack keys sitting unused #7149

                Description

                @os-warren

                Split out of #7142, which fixed one literal in packages/plugin-detail/src/ActivityTimeline.tsx (the empty-state title). Sweeping the file while implementing that fix found the empty state was not a lone oversight: the component uses no translation hook at all on origin/main — it imported nothing from @object-ui/i18n and made no t() call anywhere. Filed rather than fixed, because widening #7142's scope was the PM's call, not the implementing agent's.

                Measured

                Rendered ActivityTimeline with activities={[]} under a zh I18nProvider, and its sibling RecordActivityTimeline the same way, reading textContent off both containers:

                [zh] ActivityTimeline => "Activity(0)No activity recorded"
                [zh] RecordActivityTimeline => "活动(0)全部动态暂无活动记录"
                [no-provider, filterable] ActivityTimeline => "Activity(0)AllField ChangesCreatesDeletesCommentsStatus ChangesNo activity recorded"
                

                The sibling renders the same surface entirely in Chinese. After #7142 the zh card reads "Activity(0)暂无活动记录" — correct for the empty state, still English for the card title beside it.

                Inventory

                Line numbers are on the post-#7142 file. "pack key" means a key whose value in the en pack is already the exact literal, read out of the pack objects directly (not grepped — the packs are nested, so a dotted-key grep returns zero for keys that exist).

                Relative timestamps, formatTimestamp:

                lineliteralexisting key
                49'just now'detail.justNow
                50`${diffMins}m ago`detail.minutesAgo = '{{count}}m ago'
                52`${diffHours}h ago`detail.hoursAgo
                54`${diffDays}d ago`detail.daysAgo

                Filter chips, FILTER_LABELS:

                lineliteralexisting key
                82'All'none
                83'Field Changes'detail.fieldChangesFilter
                84'Creates'none
                85'Deletes'none
                86'Comments'detail.comments
                87'Status Changes'none

                Card chrome:

                lineliteralexisting key
                109Activity (the CardTitle text node)detail.activity
                118aria-label="Activity type filter"none (detail.filterActivity = 'Filter activity' is close but not identical)

                Activity descriptions, formatFieldChange — sentences assembled in code, so these need new keys with interpolation holes rather than a lookup:

                lineliteralexisting key
                66, 67, 74'(empty)'none
                68`Changed ${fieldLabel} from "${oldVal}" to "${newVal}"`none
                71'Created this record'none
                72'Deleted this record'none
                75`Changed status to "${newVal}"`none
                78'Updated record'none

                So 12 of the 18 are a lookup swap against keys that already exist and are already translated in all ten packs; the remaining 6 (plus the two chip labels and the aria label) need new keys across en.ts and its nine siblings, with all-locales-key-parity green. Never an inline defaultValue (#3517).

                Reachability caveat, worth checking before sizing this

                DetailView gates the timeline on hasActivity = !!schema.activities && schema.activities.length > 0 (DetailView.tsx:1408) and never passes filterable, so from that host the filter chips never render and the empty state is unreachable. The chips and the filtered-empty path are reachable through the published export (packages/plugin-detail/src/index.tsx:118) by a consumer passing filterable. That may change how this is prioritized, but it does not change the timestamps or the card title, which render on every activity tab.

                Unrelated observation in the same file

                Filter is imported from lucide-react at line 11 and never used — eslint reports it as a warning today (@typescript-eslint/no-unused-vars, warning not error, so nothing is red).

                Metadata

                Metadata

                Assignees

                Labels

                domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatfinding

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions