rename newswire and clean wire text
Some checks failed
CI / Validate (pull_request) Failing after 18s
Some checks failed
CI / Validate (pull_request) Failing after 18s
This commit is contained in:
parent
0320533628
commit
38dcf73c44
5 changed files with 79 additions and 31 deletions
|
|
@ -30,6 +30,7 @@
|
|||
{"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-9gb","title":"Rename news route to Newswire","description":"Follow-up to the mock9 production terminal rebuild: rename the /news route title from Wire Control to Newswire and keep the visual verification/docs aligned with the latest user-facing label.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-06-13T14:33:30Z","created_by":"dirtydishes","updated_at":"2026-06-13T14:37:01Z","started_at":"2026-06-13T14:33:42Z","closed_at":"2026-06-13T14:37:01Z","close_reason":"Renamed the /news route to Newswire, updated the design record and turn document, decoded common provider HTML entities in news text, and validated with focused web tests, production build, and Helium fitted/narrow inspection.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-iil","title":"Replace overview with dashboard command page","description":"Turn the mock9 Market Command concept into the production root dashboard, rename the visible route from Home to Dashboard, and keep the layout dense with a chart-first command surface.","acceptance_criteria":"Root page displays Dashboard instead of Home; dashboard includes command metrics, chart area, decision levels, priority board, live context, feed health, dark context, and replay context; web tests and production build pass.","notes":"Implemented from the mock9 direction while preserving the existing / URL and using the existing ChartPane until proper chart implementation lands.","status":"closed","priority":2,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-06-13T07:37:56Z","created_by":"dirtydishes","updated_at":"2026-06-13T07:43:44Z","started_at":"2026-06-13T07:38:02Z","closed_at":"2026-06-13T07:43:44Z","close_reason":"dashboard replacement implemented, validated, and documented","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-7l2","title":"Configure local web and desktop to use hosted Islandflow API","description":"Local web development and the Electron desktop shell are not connecting to the VPS-hosted API reliably after a recent endpoint change. Verify the active Delta Island API hostname, update local/default configuration so bun run dev:web and desktop development target it correctly, and validate the relevant web/desktop paths.","status":"closed","priority":2,"issue_type":"bug","owner":"dishes@dpdrm.com","created_at":"2026-06-13T07:32:28Z","created_by":"dirtydishes","updated_at":"2026-06-13T07:38:19Z","closed_at":"2026-06-13T07:38:19Z","close_reason":"Configured local web and desktop development to use https://api.flow.deltaisland.io as the hosted API origin, updated docs and local ignored env, verified the API host from the VPS, passed focused tests, public API route checks, and web build. Dev-web smoke confirmed the corrected API origin but port 3000 was already occupied.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-4j7","title":"replace activity matrix with alert lineage mock","description":"Rework the confusing Activity Matrix mock into a concrete alert lineage view that shows how a selected alert formed, including evidence chain, confirming/against context, invalidations, and audit state.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-06-12T00:10:18Z","created_by":"dirtydishes","updated_at":"2026-06-12T00:15:45Z","started_at":"2026-06-12T00:10:21Z","closed_at":"2026-06-12T00:15:45Z","close_reason":"Replaced the abstract Activity Matrix mock with an alert lineage view that supports all-symbol scope, selected alert evidence, invalidations, and audit context.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ This system explicitly rejects the anti-references in PRODUCT.md: no meme-stock
|
|||
- Accent color treated as scarce signal.
|
||||
- Monospace-assisted precision for time, numeric, and status data.
|
||||
- Readability preserved during bursty live updates.
|
||||
- Route-specific signatures: Dashboard is Market Command, Options is OPRA Intake, News is Wire Control.
|
||||
- Route-specific signatures: Dashboard is Market Command, Options is OPRA Intake, News is Newswire.
|
||||
- Flat terminal sections: border-block dividers and compact headers are the default; rounded cards are not.
|
||||
|
||||
## Colors
|
||||
|
|
@ -203,7 +203,7 @@ The system is flat by default. Depth is primarily tonal (background and border d
|
|||
|
||||
- **Dashboard / Market Command:** command metrics, priority board, decision levels, chart context, source health, recent contracts, replay state, and evidence context in one dense operating board.
|
||||
- **Options / OPRA Intake:** production `OptionsPane` and `FlowPane` remain the source of truth, with TanStack virtual rows, contract focus, scroll gates, and filters tuned for option decision work.
|
||||
- **News / Wire Control:** virtualized wire rows, source rails, symbol rails, live-only state, older-history scroll gates, and the existing news drawer.
|
||||
- **News / Newswire:** virtualized wire rows, source rails, symbol rails, live-only state, older-history scroll gates, and the existing news drawer.
|
||||
|
||||
### Inputs / Fields
|
||||
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ const {
|
|||
composeTapeItems,
|
||||
deriveAlertDirection,
|
||||
countActiveFlowFilterGroups,
|
||||
decodeNewsText,
|
||||
filterOptionTapeItems,
|
||||
findAnchorRestoreIndex,
|
||||
formatCompactUsd,
|
||||
|
|
@ -562,6 +563,20 @@ describe("fixed tape virtualization config", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("news text formatting", () => {
|
||||
it("decodes common html entities in provider text", () => {
|
||||
expect(
|
||||
decodeNewsText(
|
||||
"Palantir CEO Alex Karp Is 'Rooting For Elon' & Clients 'Screaming'"
|
||||
)
|
||||
).toBe("Palantir CEO Alex Karp Is 'Rooting For Elon' & Clients 'Screaming'");
|
||||
});
|
||||
|
||||
it("leaves unknown entities untouched", () => {
|
||||
expect(decodeNewsText("Keep &market; literal")).toBe("Keep &market; literal");
|
||||
});
|
||||
});
|
||||
|
||||
describe("dark underlying route dependency helper", () => {
|
||||
it("does not keep extra equities subscriptions when joins+trace fallback are sufficient", () => {
|
||||
expect(shouldIncludeEquitiesForDarkUnderlyingFallback()).toBe(false);
|
||||
|
|
|
|||
|
|
@ -1277,15 +1277,45 @@ export const formatNewsTimestamp = (ts: number, now = Date.now()): string => {
|
|||
});
|
||||
};
|
||||
|
||||
const NEWS_TEXT_ENTITIES: Record<string, string> = {
|
||||
amp: "&",
|
||||
apos: "'",
|
||||
gt: ">",
|
||||
lt: "<",
|
||||
nbsp: " ",
|
||||
quot: '"'
|
||||
};
|
||||
|
||||
export const decodeNewsText = (value: string): string =>
|
||||
value.replace(/&(#\d+|#x[\da-f]+|[a-z][\da-z]+);/gi, (match, entity: string) => {
|
||||
if (entity[0] === "#") {
|
||||
const radix = entity[1]?.toLowerCase() === "x" ? 16 : 10;
|
||||
const rawCodePoint = radix === 16 ? entity.slice(2) : entity.slice(1);
|
||||
const codePoint = Number.parseInt(rawCodePoint, radix);
|
||||
if (!Number.isFinite(codePoint)) {
|
||||
return match;
|
||||
}
|
||||
try {
|
||||
return String.fromCodePoint(codePoint);
|
||||
} catch {
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
return NEWS_TEXT_ENTITIES[entity.toLowerCase()] ?? match;
|
||||
});
|
||||
|
||||
const sanitizeNewsHtml = (
|
||||
value: string
|
||||
): { html: string; fallbackText: string; sanitized: boolean } => {
|
||||
const fallbackText = value
|
||||
const fallbackText = decodeNewsText(
|
||||
value
|
||||
.replace(/<script[\s\S]*?<\/script>/gi, " ")
|
||||
.replace(/<style[\s\S]*?<\/style>/gi, " ")
|
||||
.replace(/<[^>]+>/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim();
|
||||
.trim()
|
||||
);
|
||||
|
||||
try {
|
||||
const sanitized = value
|
||||
|
|
@ -5017,13 +5047,15 @@ type NewsDrawerProps = {
|
|||
|
||||
const NewsDrawer = ({ story, onClose }: NewsDrawerProps) => {
|
||||
const body = sanitizeNewsHtml(story.content_html);
|
||||
const headline = decodeNewsText(story.headline);
|
||||
const summary = decodeNewsText(story.summary);
|
||||
|
||||
return (
|
||||
<aside className="drawer">
|
||||
<div className="drawer-header">
|
||||
<div>
|
||||
<p className="drawer-eyebrow">News wire</p>
|
||||
<h3>{story.headline}</h3>
|
||||
<h3>{headline}</h3>
|
||||
<p className="drawer-subtitle">
|
||||
{story.source} · Published {formatDateTime(story.published_ts)}
|
||||
{story.updated_ts !== story.published_ts
|
||||
|
|
@ -5045,10 +5077,10 @@ const NewsDrawer = ({ story, onClose }: NewsDrawerProps) => {
|
|||
<span className="drawer-chip">{story.symbol_resolution}</span>
|
||||
</div>
|
||||
|
||||
{story.summary ? (
|
||||
{summary ? (
|
||||
<div className="drawer-section">
|
||||
<h4>Summary</h4>
|
||||
<p className="drawer-note">{story.summary}</p>
|
||||
<p className="drawer-note">{summary}</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
|
|
@ -8413,6 +8445,8 @@ const NewsPane = memo(({ state, limit, className }: NewsPaneProps) => {
|
|||
>
|
||||
{virtual.virtualItems.map(({ item: story, key, index, start, size }) => {
|
||||
const wireStatus = getNewsWireStatus(story);
|
||||
const headline = decodeNewsText(story.headline);
|
||||
const summary = decodeNewsText(story.summary || story.provider);
|
||||
return (
|
||||
<button
|
||||
className={`data-table-row data-table-row-button data-table-row-news data-table-virtual-row${index % 2 === 1 ? " is-even" : ""} news-wire-row-${wireStatus}`}
|
||||
|
|
@ -8435,10 +8469,8 @@ const NewsPane = memo(({ state, limit, className }: NewsPaneProps) => {
|
|||
{wireStatus}
|
||||
</span>
|
||||
</span>
|
||||
<span className="data-table-cell news-headline-cell">{story.headline}</span>
|
||||
<span className="data-table-cell news-summary-cell">
|
||||
{story.summary || story.provider}
|
||||
</span>
|
||||
<span className="data-table-cell news-headline-cell">{headline}</span>
|
||||
<span className="data-table-cell news-summary-cell">{summary}</span>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
|
|
@ -8961,7 +8993,7 @@ const buildCommandPriorityRows = (state: TerminalState): CommandPriorityRow[] =>
|
|||
ts: story.published_ts,
|
||||
symbol: story.resolved_symbols[0]?.toUpperCase() ?? "WIRE",
|
||||
packet: story.source,
|
||||
read: story.headline,
|
||||
read: decodeNewsText(story.headline),
|
||||
score: story.resolved_symbols.length > 0 ? 55 : 25,
|
||||
invalidation: getNewsWireStatus(story),
|
||||
state: "info",
|
||||
|
|
@ -9272,7 +9304,7 @@ const EventContextPane = ({ state }: { state: TerminalState }) => {
|
|||
key: `news-${story.trace_id}-${story.seq}`,
|
||||
ts: story.published_ts,
|
||||
label: "News",
|
||||
title: story.headline,
|
||||
title: decodeNewsText(story.headline),
|
||||
detail: story.resolved_symbols.length > 0 ? story.resolved_symbols.join(", ") : story.source,
|
||||
action: () => state.setSelectedNewsStory(story)
|
||||
}))
|
||||
|
|
@ -10214,7 +10246,7 @@ export function OverviewRoute() {
|
|||
export function NewsRoute() {
|
||||
const state = useTerminal();
|
||||
return (
|
||||
<PageFrame title="Wire Control" eyebrow="News" variant="news">
|
||||
<PageFrame title="Newswire" eyebrow="News" variant="news">
|
||||
<div className="wire-control-shell">
|
||||
<NewsControlRails state={state} />
|
||||
<NewsPane state={state} className="news-pane-full" />
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue