{"compiledCode":"\"use strict\";Object.defineProperty(exports, \"__esModule\", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _react = require('react'); var _react2 = _interopRequireDefault(_react);\n\n function safeDestination(value) {\n  if (typeof value !== \"string\") return null;\n  try { const url = new URL(value.trim()); return !url.username && !url.password && [\"https:\", \"http:\"].includes(url.protocol) ? url.href : null; } catch (e) { return null; }\n} exports.safeDestination = safeDestination;\n function chooseNext(items, seen, previous, random = Math.random) {\n  let candidates = items.filter(item => !seen.has(item.id));\n  if (!candidates.length) { seen.clear(); candidates = items.filter(item => items.length === 1 || item.id !== previous); }\n  if (!candidates.length) return null;\n  const chosen = candidates[Math.floor(random() * candidates.length)];\n  seen.add(chosen.id); return chosen;\n} exports.chooseNext = chooseNext;\n async function loadDiscoveries(request = fetch, signal) {\n  const items = []; const ids = new Set(); let offset = 0;\n  for (let page = 0; page < 100; page++) {\n    const response = await request(`/extensions/cedros-directory/public/directories/main/listings?limit=100&offset=${offset}`, {credentials:\"omit\", signal});\n    if (!response.ok) throw new Error(\"The directory could not be loaded.\");\n    const payload = await response.json(); const data = _optionalChain([payload, 'optionalAccess', _ => _.data]);\n    if (_optionalChain([payload, 'optionalAccess', _2 => _2.ok]) !== true || !Array.isArray(_optionalChain([data, 'optionalAccess', _3 => _3.listings])) || typeof _optionalChain([data, 'optionalAccess', _4 => _4.pagination, 'optionalAccess', _5 => _5.hasMore]) !== \"boolean\") throw new Error(\"The directory returned an incomplete result.\");\n    for (const entry of data.listings) {\n      if (typeof _optionalChain([entry, 'optionalAccess', _6 => _6.id]) !== \"string\" || typeof _optionalChain([entry, 'optionalAccess', _7 => _7.title]) !== \"string\") throw new Error(\"The directory returned an incomplete listing.\");\n      const website = safeDestination(_optionalChain([entry, 'access', _8 => _8.contact, 'optionalAccess', _9 => _9.website]));\n      if (website && !ids.has(entry.id)) { ids.add(entry.id); items.push({...entry, publicFields:Array.isArray(entry.publicFields) ? entry.publicFields : [], website}); }\n    }\n    if (!data.pagination.hasMore) return items;\n    const next = data.pagination.nextOffset;\n    if (!Number.isSafeInteger(next) || next <= offset) throw new Error(\"The directory could not be read completely.\");\n    offset = next;\n  }\n  throw new Error(\"The directory is too large for this discovery view. Browse the directory instead.\");\n} exports.loadDiscoveries = loadDiscoveries;\nfunction Discovery() {\n  const [state, setState] = _react2.default.useState({loading:true, error:\"\", items:[] });\n  const [current, setCurrent] = _react2.default.useState(null);\n  const [attempt, setAttempt] = _react2.default.useState(0);\n  const seen = _react2.default.useRef(new Set());\n  _react2.default.useEffect(() => {\n    const controller = new AbortController(); let active = true;\n    setState({loading:true,error:\"\",items:[]}); setCurrent(null); seen.current.clear();\n    loadDiscoveries(fetch, controller.signal).then(items => { if(active) setState({loading:false,error:\"\",items}); }).catch(error => { if(active && error.name !== \"AbortError\") setState({loading:false,error:\"We could not load discoveries. Please retry or browse the directory.\",items:[]}); });\n    return () => { active=false; controller.abort(); };\n  }, [attempt]);\n  return _react2.default.createElement('section', { className: \"live-discovery\", 'aria-labelledby': \"discover-heading\",}\n    , _react2.default.createElement('h2', { id: \"discover-heading\",}, \"One good find at a time.\"     )\n    , _react2.default.createElement('div', { 'aria-live': \"polite\", 'aria-atomic': \"true\",}\n      , state.loading ? _react2.default.createElement('p', null, \"Loading the published directory…\"   ) : state.error ? _react2.default.createElement('p', { role: \"alert\",}, state.error) : !state.items.length ? _react2.default.createElement(_react2.default.Fragment, null, _react2.default.createElement('p', null, \"The directory is ready for its first approved listings.\"        ), _react2.default.createElement('p', null, \"There are no published entries with a public website to show yet. Every suggestion goes through editorial review.\"                 ), _react2.default.createElement('a', { href: \"/directory/submit\",}, \"Suggest a listing\"  )) : current ? _react2.default.createElement('article', null, _react2.default.createElement('h3', null, current.title), _react2.default.createElement('p', null, typeof current.summary === \"string\" ? current.summary : \"Visit the original site to explore this listing.\"), current.publicFields.filter((field) => field && /ownership|publisher|disclosure/i.test(String(field.label || field.key))).map((field,index) => _react2.default.createElement('p', { key: index,}, String(field.label || \"Disclosure\"), \": \" , String(_nullishCoalesce(field.value, () => ( \"\"))))), _react2.default.createElement('a', { href: current.website, target: \"_blank\", rel: \"noopener noreferrer\" ,}, \"Visit the original website ↗\"    ), typeof current.slug === \"string\" && _react2.default.createElement('p', null, _react2.default.createElement('a', { href: `/directory/listing?listing=${encodeURIComponent(current.slug)}`,}, \"Read the full listing and disclosures\"     ))) : _react2.default.createElement('p', null, \"Choose a surprise from the published directory. Placement, popularity, and payment do not affect the draw.\"               )\n    )\n    , state.error ? _react2.default.createElement('button', { type: \"button\", onClick: () => setAttempt(n=>n+1),}, \"Retry discoveries\" ) : state.items.length > 0 && _react2.default.createElement('button', { type: \"button\", onClick: () => setCurrent(chooseNext(state.items,seen.current,_nullishCoalesce(_optionalChain([current, 'optionalAccess', _10 => _10.id]), () => ( null)))),}, current ? \"Show me another\" : \"Show me a discovery\")\n    , _react2.default.createElement('p', null, _react2.default.createElement('a', { href: \"/directory\",}, \"Browse all published listings\"   ))\n  );\n}\n\n function ReadingPage(){return _react2.default.createElement('main', { className: \"site-reading\",}, _react2.default.createElement('p', { className: \"eyebrow\",}, \"Uptism\"), _react2.default.createElement('h1', null, \"Stumble: one hopeful find at a time\"      ), _react2.default.createElement(Discovery, null), _react2.default.createElement('div', { dangerouslySetInnerHTML: {__html:\"\\n<p>A worthwhile surprise should leave you with context, not just another link. Use the discovery button to draw from approved, published directory entries with public website links. Explore the reading routes below for more context.</p>\\n<p>Explore the searchable <a href=\\\"/directory\\\">directory</a>, try <a href=\\\"/stumble\\\">Stumble</a>, or <a href=\\\"/directory/submit\\\">suggest a find</a>. Every submission requires editorial review before publication. The category hubs and collections are reading guides. There is no email newsletter or promised delivery schedule.</p>\\n<h2>What a useful discovery would provide</h2>\\n<ol>\\n<li><strong>One clearly framed find.</strong> Explain what it is and why it deserves attention without engagement bait.</li>\\n<li><strong>The evidence behind the enthusiasm.</strong> Name the audience, maturity, limitations and assessment date. A scraped URL is not a curated entry.</li>\\n<li><strong>A direct way to investigate.</strong> Let the reader inspect the original source and supporting context, or leave without friction.</li>\\n</ol>\\n<p>The <a href=\\\"/directory-guide\\\">directory schema</a> defines the required fields. <a href=\\\"/how-we-curate\\\">How we curate</a> explains the selection bar. The pool contains only published directory entries. It is empty until a find has passed editorial review.</p>\\n<h2>Curated means a person can explain the choice</h2>\\n<p>A conforming find must be real, honestly described and relevant to a better future. Trade-offs and unresolved questions belong beside the promise. No fabricated rating, review or award substitutes for that explanation.</p>\\n<p>Placement is never for sale. There should be no paid boost, token promotion or undisclosed relationship in the editorial selection. See the <a href=\\\"/disclosure\\\">disclosure</a>.</p>\\n<h2>Wander by theme</h2>\\n<ul>\\n<li><a href=\\\"/category/ai\\\">Frontier AI</a> — read about capabilities and their limits.</li>\\n<li><a href=\\\"/category/hard-tech\\\">Hard tech</a> — explore robotics, space, manufacturing and materials.</li>\\n<li><a href=\\\"/category/climate\\\">Climate and abundance</a> — consider energy and a buildable future.</li>\\n<li><a href=\\\"/category/bio\\\">Biotech</a> — keep evidence and health boundaries in view.</li>\\n</ul>\\n<p>These are editorial hubs. They do not narrow the discovery pool; the button draws uniformly from all eligible published entries. The <a href=\\\"/reading-collections\\\">collection framework</a> explains how a themed reading group should be presented.</p>\\n<h2>Make serendipity a reading habit</h2>\\n<p>Read <a href=\\\"/blog/stumbleupon-we-lost\\\">the StumbleUpon we lost</a> for what discovery can give readers, or <a href=\\\"/blog/discovery-habit-beats-the-feed\\\">the discovery habit beats the feed</a> for a repeatable way to explore.</p>\\n<p>Bookmark a useful hub and return on your own schedule. The <a href=\\\"/newsletter\\\">updates reading map</a> has no signup or recurring delivery. No account or profile is needed to read these pages.</p>\\n<h2>Keep optimism grounded</h2>\\n<p>Start with <a href=\\\"/optimism\\\">the case for optimism</a> if you want the point of view first. Uptism is an editorial brand, not a token, game or investment offer. No historical memecoin connection or inherited community is claimed.</p>\\n<h2>Current implementation</h2>\\n<p>The discovery view reads the complete public listing pool when this page opens. It draws uniformly from entries with valid public website links, avoids repeats within a round, and starts a new round when exhausted. State stays in this page’s memory and resets on reload. No payment, featured flag, click history, account, or reader profile weights the draw.</p>\\n<p>Editors must unpublish an entry if its evidence is stale, disputed, or subject to removal. No automated check-log or age-based suspension is claimed. Reload to pick up publication changes. The view fails visibly rather than drawing from a partial pool if it cannot load all pages.</p>\\n\"},}), _react2.default.createElement('style', null, \".site-reading{max-width:1100px;margin:auto;padding:clamp(24px,5vw,64px) clamp(18px,4vw,40px);color:var(--cuqjpvh-runtime-text,inherit);line-height:1.75;overflow-wrap:anywhere}.site-reading h1,.site-reading h2,.site-reading h3{font-family:var(--cuqjpvh-font-heading,Georgia,serif);line-height:1.16}.site-reading h1{font-size:clamp(36px,6vw,64px);letter-spacing:-.035em}.site-reading h2{font-size:clamp(26px,4vw,34px);margin-top:2em}.site-reading p,.site-reading li{max-width:820px}.site-reading a{color:var(--cuqjpvh-runtime-accent,inherit);text-underline-offset:4px}.site-reading a:focus-visible,.site-reading button:focus-visible{outline:3px solid currentColor;outline-offset:4px}.site-reading table{border-collapse:collapse;width:100%;display:block;overflow-x:auto}.site-reading th,.site-reading td{padding:14px;border:1px solid var(--cuqjpvh-runtime-border,#b7b7ab);text-align:left;min-width:140px}.site-reading .live-discovery{padding:clamp(20px,4vw,40px);border:1px solid var(--cuqjpvh-runtime-border,#b7b7ab);background:var(--cuqjpvh-runtime-panel,transparent);border-radius:16px;margin-block:24px 40px}.site-reading .live-discovery h2{margin-top:0}.site-reading button{font:inherit;cursor:pointer;min-height:48px;padding:12px 22px;background:var(--cuqjpvh-runtime-action-bg,#2d6545);color:var(--cuqjpvh-runtime-action-fg,#fff);border:0;border-radius:8px}.site-reading blockquote{margin-left:0;border-left:3px solid currentColor;padding-left:22px}.site-reading .eyebrow{text-transform:uppercase;letter-spacing:.14em;font-size:12px;font-weight:700}\"));} exports.default = ReadingPage;"}