← back to Microsite Engine
ideas dropdown now carries ALL 267 owned domains in three tiers
0c12ece84286f75947f1f5f0898983f25cebd9a1 · 2026-07-17 10:24:54 -0700 · Steve Abrams
- tier 1: niche-matched ideas (domain × catalog tag, product counts, ranked)
- tier 2: every other owned non-expired domain, alphabetical
- tier 3: domains already used by the live fleet, labeled
- picking any domain prefills slug/domain/site name; niche keywords + tagline
only prefill when there's a real catalog match
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Files touched
M public/index.htmlM server.js
Diff
commit 0c12ece84286f75947f1f5f0898983f25cebd9a1
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Fri Jul 17 10:24:54 2026 -0700
ideas dropdown now carries ALL 267 owned domains in three tiers
- tier 1: niche-matched ideas (domain × catalog tag, product counts, ranked)
- tier 2: every other owned non-expired domain, alphabetical
- tier 3: domains already used by the live fleet, labeled
- picking any domain prefills slug/domain/site name; niche keywords + tagline
only prefill when there's a real catalog match
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---
public/index.html | 21 ++++++++++++++-------
server.js | 26 ++++++++++++++++----------
2 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/public/index.html b/public/index.html
index 3e27d0e..f3a6548 100644
--- a/public/index.html
+++ b/public/index.html
@@ -220,16 +220,23 @@ async function boot(){
const d=$('domain').value.trim().toLowerCase();
if(doms.includes(d) && !$('slug').value.trim()) $('slug').value=d.split('.')[0].replace(/[^a-z0-9-]/g,'');
});
- // "start from a site idea" — each idea pairs an owned unused domain with the
- // catalog tags found inside its name (real product counts)
- $('ideaAdd').innerHTML = '<option value="">💡 pick an idea to prefill the form…</option>' +
- (FACETS.ideas||[]).map((it,i)=>`<option value="${i}">${esc(it.domain)} — ${esc(it.keywords.join(', '))} · ${it.count.toLocaleString()} products</option>`).join('');
+ // "start from a site idea" — EVERY owned domain, in three tiers: niche-matched
+ // ideas (with real product counts), all other owned domains, then fleet-taken
+ const IDEAS = FACETS.ideas||[];
+ const opt = (it,i)=>`<option value="${i}">${esc(it.domain)}${it.keywords.length?` — ${esc(it.keywords.join(', '))} · ${it.count.toLocaleString()} products`:''}</option>`;
+ const grp = (label,f)=>{ const o=IDEAS.map((it,i)=>f(it)?opt(it,i):'').join(''); return o?`<optgroup label="${label}">${o}</optgroup>`:''; };
+ $('ideaAdd').innerHTML = '<option value="">💡 pick a domain / idea to prefill the form…</option>'
+ + grp('Niche ideas — domain × catalog match', it=>it.keywords.length&&!it.inFleet)
+ + grp('All owned domains', it=>!it.keywords.length&&!it.inFleet)
+ + grp('Already in the fleet', it=>it.inFleet);
$('ideaAdd').onchange = ()=>{
- const it=(FACETS.ideas||[])[+$('ideaAdd').value];
+ const it=IDEAS[+$('ideaAdd').value];
if(!it) return;
$('slug').value=it.slug; $('domain').value=it.domain; $('siteName').value=it.siteName;
- $('tagline').value=`Curated ${it.keywords[0].toLowerCase()} wallcoverings for designers and discerning homes.`;
- $('pos').value=it.keywords.join(', ');
+ if(it.keywords.length){
+ $('tagline').value=`Curated ${it.keywords[0].toLowerCase()} wallcoverings for designers and discerning homes.`;
+ $('pos').value=it.keywords.join(', ');
+ }
document.querySelectorAll('#tagChips .chip').forEach(c=>c.classList.toggle('on',it.keywords.some(k=>k.toLowerCase()===c.dataset.tag.toLowerCase())));
syncHeroText(); doPreview();
};
diff --git a/server.js b/server.js
index ba33bdd..c83011d 100644
--- a/server.js
+++ b/server.js
@@ -110,7 +110,8 @@ function segmentBase(base, vocab) {
}
function domainIdeas() {
const taken = new Set(liveSites().map(s => (s.domain || '').toLowerCase()));
- const free = ownedDomains().filter(d => !taken.has(d));
+ const owned = ownedDomains();
+ const free = owned.filter(d => !taken.has(d));
const tagByLower = new Map();
for (const t of FACETS.tagList) {
const k = t.name.toLowerCase();
@@ -119,25 +120,30 @@ function domainIdeas() {
}
const vocab = [...new Set([...tagByLower.keys(), ...FILLER_WORDS])]
.filter(w => w.length >= 3).sort((a, b) => b.length - a.length);
- const ideas = [];
- for (const dom of free) {
+ // EVERY owned domain is selectable to start a process; ones whose name
+ // contains a real niche tag rank first (with product counts), the rest
+ // follow alphabetically, and fleet-taken domains sit last, labeled.
+ const matched = [], plain = [];
+ for (const dom of owned) {
const base = dom.split('.')[0].replace(/[^a-z0-9]/g, '');
const words = segmentBase(base, vocab);
// filler words (wallpaper/wallcovering/designer/…) help segmentation but are
- // not a niche — an idea needs at least one REAL niche tag
+ // not a niche — a ranked idea needs at least one REAL niche tag
const tags = words.filter(w => !FILLER_WORDS.includes(w.replace(/s$/, '')) && !FILLER_WORDS.includes(w))
.map(w => tagByLower.get(w)).filter(Boolean);
- if (!tags.length) continue;
- ideas.push({
+ const idea = {
domain: dom,
slug: base,
siteName: words.map(w => DISPLAY_FIX[w] || (w[0].toUpperCase() + w.slice(1))).join(' '),
keywords: tags.map(t => t.name),
- count: Math.max(...tags.map(t => t.n)),
- });
+ count: tags.length ? Math.max(...tags.map(t => t.n)) : 0,
+ inFleet: taken.has(dom),
+ };
+ if (tags.length && !idea.inFleet) matched.push(idea); else plain.push(idea);
}
- ideas.sort((a, b) => b.count - a.count);
- return { domains: free, ideas: ideas.slice(0, 60) };
+ matched.sort((a, b) => b.count - a.count);
+ plain.sort((a, b) => (a.inFleet - b.inFleet) || a.domain.localeCompare(b.domain));
+ return { domains: free, ideas: [...matched, ...plain] };
}
// ---- read the LIVE fleet's existing sites (READ-ONLY) for collision guard ----
← d37b802 fix idea site-name segmentation: DP coverage-max splitter +
·
back to Microsite Engine
·
ownedDomains: read newer CNCP entries keyed by 'domain' (not 191665d →