mcdview 0.26.0__tar.gz → 0.26.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mcdview
3
- Version: 0.26.0
3
+ Version: 0.26.2
4
4
  Summary: Turn a SQL data model into one self-contained, interactive HTML page
5
5
  Project-URL: Homepage, https://github.com/Gheop/mcdview
6
6
  Project-URL: Demo, https://gheop.github.io/mcdview/
@@ -323,6 +323,24 @@ file's header.
323
323
 
324
324
  ## Changelog
325
325
 
326
+ ### v0.26.2 — Auto-dismiss the hint banner (2026-08-30)
327
+
328
+ - The transient hint banner (e.g. "No probable missing FK", "No match", a
329
+ cycle/type/ref count) now clears itself after ~2.6 s, so it no longer sits
330
+ over the menus that open below it. The join-path prompts stay put until you
331
+ pick the two tables.
332
+
333
+ ### v0.26.1 — Parse inline-opening multi-line CREATE TABLE (2026-08-30)
334
+
335
+ - The built-in PostgreSQL parser now reads a `CREATE TABLE` whose `(` is
336
+ followed by columns on the same line and whose body continues on later lines
337
+ (closing `)` elsewhere) — e.g. `CREATE TABLE t (id int,\n FOREIGN KEY …\n);`.
338
+ Previously it sat between the two fast passes and was silently dropped in a
339
+ mixed file (only rescued by sqlglot when the whole file had zero
340
+ regex-parseable tables). The single-line pass now extends to the statement's
341
+ closing `);` within a bounded window (a C-level search, no copy), so the
342
+ ReDoS/DoS budget is unchanged (the orphan-openers stress stays ~0.4 s).
343
+
326
344
  ### v0.26.0 — SVG preview, view deep-links, schema lint (2026-08-30)
327
345
 
328
346
  - `--to-preview` emits a static SVG snapshot of the diagram (auto-layout,
@@ -301,6 +301,24 @@ file's header.
301
301
 
302
302
  ## Changelog
303
303
 
304
+ ### v0.26.2 — Auto-dismiss the hint banner (2026-08-30)
305
+
306
+ - The transient hint banner (e.g. "No probable missing FK", "No match", a
307
+ cycle/type/ref count) now clears itself after ~2.6 s, so it no longer sits
308
+ over the menus that open below it. The join-path prompts stay put until you
309
+ pick the two tables.
310
+
311
+ ### v0.26.1 — Parse inline-opening multi-line CREATE TABLE (2026-08-30)
312
+
313
+ - The built-in PostgreSQL parser now reads a `CREATE TABLE` whose `(` is
314
+ followed by columns on the same line and whose body continues on later lines
315
+ (closing `)` elsewhere) — e.g. `CREATE TABLE t (id int,\n FOREIGN KEY …\n);`.
316
+ Previously it sat between the two fast passes and was silently dropped in a
317
+ mixed file (only rescued by sqlglot when the whole file had zero
318
+ regex-parseable tables). The single-line pass now extends to the statement's
319
+ closing `);` within a bounded window (a C-level search, no copy), so the
320
+ ReDoS/DoS budget is unchanged (the orphan-openers stress stays ~0.4 s).
321
+
304
322
  ### v0.26.0 — SVG preview, view deep-links, schema lint (2026-08-30)
305
323
 
306
324
  - `--to-preview` emits a static SVG snapshot of the diagram (auto-layout,
@@ -271,14 +271,24 @@ def analyser_sql(chemin):
271
271
  cle = f"{m.group(1) or 'public'}.{m.group(2)}"
272
272
  if cle in tables: # already built by the multi-line pass
273
273
  continue
274
- # slice only to the end of the opener's line, not the whole remaining
275
- # file (else 3000 unterminated openers each copy the full tail — a DoS)
274
+ # first try the opener's own line only — the common single-line table,
275
+ # and cheap even on a "CREATE TABLE a (\n" bomb (empty body)
276
276
  fin_ligne = src.find('\n', m.end())
277
- ligne_reste = src[m.end():fin_ligne if fin_ligne != -1 else len(src)]
278
- entrees, ok = decouper_corps(ligne_reste)
277
+ corps_txt = src[m.end():fin_ligne if fin_ligne != -1 else len(src)]
278
+ entrees, ok = decouper_corps(corps_txt)
279
+ if not ok:
280
+ # the body runs past the opener's line (e.g. "(id int,\n FOREIGN KEY
281
+ # …\n);"). Look for a plausible statement close ");" within a bounded
282
+ # window — a C-level find with no copy, so many unterminated openers
283
+ # stay cheap — then depth-split only that slice
284
+ borne = min(len(src), m.end() + 20000)
285
+ j = src.find(');', m.end(), borne)
286
+ if j != -1:
287
+ corps_txt = src[m.end():j + 1]
288
+ entrees, ok = decouper_corps(corps_txt)
279
289
  if ok and entrees:
280
290
  tables[cle] = entrees_en_table(m.group(1) or 'public', m.group(2), entrees)
281
- corps_par_cle[cle] = ligne_reste
291
+ corps_par_cle[cle] = corps_txt
282
292
 
283
293
  # partitions: constraints carried by a partition are folded back into
284
294
  # the parent, and the partitions themselves do not appear in the model
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "mcdview"
7
- version = "0.26.0"
7
+ version = "0.26.2"
8
8
  description = "Turn a SQL data model into one self-contained, interactive HTML page"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -1135,8 +1135,18 @@ function arcEntre(a, b) { // the FK connecting a and b, either direction
1135
1135
  (f.de === b && f.vers === a)) || null;
1136
1136
  }
1137
1137
  const indice = document.getElementById("indice");
1138
- function montrerIndice(txt) { indice.textContent = txt; indice.hidden = false; }
1139
- function cacherIndice() { indice.hidden = true; }
1138
+ let indiceTimer = null;
1139
+ function montrerIndice(txt, persistant) {
1140
+ indice.textContent = txt; indice.hidden = false;
1141
+ if (indiceTimer) { clearTimeout(indiceTimer); indiceTimer = null; }
1142
+ // transient info (no result, no cycle…) clears itself so it never sits over
1143
+ // the menus that open below it; a persistent prompt (join mode) stays put
1144
+ if (!persistant) indiceTimer = setTimeout(cacherIndice, 2600);
1145
+ }
1146
+ function cacherIndice() {
1147
+ if (indiceTimer) { clearTimeout(indiceTimer); indiceTimer = null; }
1148
+ indice.hidden = true;
1149
+ }
1140
1150
  // French UI strings built at runtime; the EN build swaps them via TRADUCTIONS
1141
1151
  const TXT = {
1142
1152
  aucuneRelation: "Aucune table liée.",
@@ -1186,7 +1196,7 @@ function choisirJointure(cle) {
1186
1196
  jointure.premier = cle;
1187
1197
  Object.keys(elems).forEach(c => elems[c].classList.remove("depart"));
1188
1198
  elems[cle].classList.add("depart");
1189
- montrerIndice(TXT.choisirSeconde);
1199
+ montrerIndice(TXT.choisirSeconde, true);
1190
1200
  } else {
1191
1201
  const a = jointure.premier, b = cle;
1192
1202
  jointure.actif = false; jointure.premier = null;
@@ -1410,7 +1420,7 @@ document.getElementById("btnJointure").addEventListener("click", () => {
1410
1420
  sortirModes(); vueGenerale();
1411
1421
  jointure.actif = true; jointure.premier = null;
1412
1422
  btn.classList.add("actif");
1413
- montrerIndice(TXT.choisirPremiere);
1423
+ montrerIndice(TXT.choisirPremiere, true);
1414
1424
  });
1415
1425
 
1416
1426
  if (!D.fks.some(f => f.audit)) document.getElementById("labelAudit").hidden = true;
File without changes
File without changes
File without changes