labull-framework 0.1.4__tar.gz → 0.1.4.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.
Files changed (47) hide show
  1. {labull_framework-0.1.4/labull_framework.egg-info → labull_framework-0.1.4.2}/PKG-INFO +1 -1
  2. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/canvas/data.py +34 -47
  3. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/canvas/static/canvas.css +100 -47
  4. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/canvas/static/canvas.js +519 -55
  5. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/canvas/static/page.html +3 -3
  6. {labull_framework-0.1.4 → labull_framework-0.1.4.2/labull_framework.egg-info}/PKG-INFO +1 -1
  7. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/pyproject.toml +1 -1
  8. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/LICENSE +0 -0
  9. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/MANIFEST.in +0 -0
  10. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/README-pypi.md +0 -0
  11. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/__init__.py +0 -0
  12. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/admin.py +0 -0
  13. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/apps.py +0 -0
  14. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/auth/__init__.py +0 -0
  15. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/auth/backend.py +0 -0
  16. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/auth/bootstrap.py +0 -0
  17. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/auth/sso.py +0 -0
  18. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/auth/views.py +0 -0
  19. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/canvas/__init__.py +0 -0
  20. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/canvas/gitinfo.py +0 -0
  21. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/canvas/views.py +0 -0
  22. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/env.py +0 -0
  23. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/jsonio.py +0 -0
  24. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/__init__.py +0 -0
  25. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/deepcheck.py +0 -0
  26. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/devkit.py +0 -0
  27. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/dispatch.py +0 -0
  28. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/export.py +0 -0
  29. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/health.py +0 -0
  30. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/registry.py +0 -0
  31. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/sdk.py +0 -0
  32. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/service.py +0 -0
  33. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/validate.py +0 -0
  34. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/magic_block/views.py +0 -0
  35. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/management/__init__.py +0 -0
  36. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/management/commands/__init__.py +0 -0
  37. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/management/commands/labull_contract.py +0 -0
  38. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/migrations/0001_initial.py +0 -0
  39. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/migrations/__init__.py +0 -0
  40. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/models.py +0 -0
  41. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/settings.py +0 -0
  42. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework/urls.py +0 -0
  43. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework.egg-info/SOURCES.txt +0 -0
  44. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework.egg-info/dependency_links.txt +0 -0
  45. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework.egg-info/requires.txt +0 -0
  46. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/labull_framework.egg-info/top_level.txt +0 -0
  47. {labull_framework-0.1.4 → labull_framework-0.1.4.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: labull-framework
3
- Version: 0.1.4
3
+ Version: 0.1.4.2
4
4
  Summary: Labull Framework: a pluggable Django app with Authing OIDC login and magic blocks
5
5
  Author: Labull Framework
6
6
  License-Expression: MIT
@@ -88,46 +88,6 @@ def _brief_line(obj: Any) -> str:
88
88
  return brief.splitlines()[0].strip() if brief else ''
89
89
 
90
90
 
91
- def _type_of(field: Any) -> str:
92
- """Django 字段 → **库里那一列的真实类型**(画布右侧那个小字)。
93
-
94
- 🔴 这里只回答"数据库里长什么样";"这个字段在 Django 里是什么"由 `_kind_of` 回答。
95
- 两者刻意分开:`FileField` 在库里就是一列 varchar,而人关心的是"这是个文件字段"。
96
- """
97
- from django.db import models
98
-
99
- mapping = (
100
- (models.BigAutoField, 'bigint'),
101
- (models.BigIntegerField, 'bigint'),
102
- (models.AutoField, 'integer'),
103
- (models.IntegerField, 'integer'),
104
- (models.SmallIntegerField, 'integer'),
105
- (models.PositiveIntegerField, 'integer'),
106
- (models.BooleanField, 'boolean'),
107
- (models.DateField, 'date'),
108
- (models.DateTimeField, 'timestamp'),
109
- (models.TimeField, 'time'),
110
- (models.DurationField, 'interval'),
111
- (models.DecimalField, 'numeric'),
112
- (models.FloatField, 'real'),
113
- (models.JSONField, 'jsonb'),
114
- (models.UUIDField, 'uuid'),
115
- (models.BinaryField, 'bytea'),
116
- (models.FileField, 'varchar'),
117
- (models.ImageField, 'varchar'),
118
- (models.EmailField, 'varchar'),
119
- (models.URLField, 'varchar'),
120
- (models.SlugField, 'varchar'),
121
- (models.GenericIPAddressField, 'inet'),
122
- (models.CharField, 'varchar'),
123
- (models.TextField, 'text'),
124
- )
125
- for cls, name in mapping:
126
- if isinstance(field, cls):
127
- return name
128
- return 'text'
129
-
130
-
131
91
  def _kind_of(field: Any) -> str:
132
92
  """Django 字段的**种类**(画布左侧那个彩色小标:枚举 / 文件 / 关联 / 金额 …)。
133
93
 
@@ -344,30 +304,57 @@ def shape() -> dict[str, list[dict[str, Any]]]:
344
304
 
345
305
 
346
306
  def _type_of(field: Any) -> str:
347
- """Django 字段 → 契约里那几种类型之一。
307
+ """Django 字段 → 契约里那几种类型之一(画布右侧那个小字:`varchar` / `text` / `bigint` …)。
308
+
309
+ 🔴 这里只回答"**库里那一列**长什么样";"这个字段在 Django 里是什么"由 `_kind_of` 回答。
310
+ 两者刻意分开:`FileField` 在库里就是一列 varchar ⇒ 这里回 `varchar`,
311
+ 而画布左边那个彩色小标由 `kind` 说了算(`文件` / `图片`)。
312
+
313
+ ⚠️ **顺序有意义**,写反了不报错、只是悄悄归错档:
314
+ · `DateTimeField` 是 `DateField` 的子类 ⇒ `created_at` 会报成 `date`(真踩过);
315
+ · `ImageField` 是 `FileField` 的子类、`EmailField` 是 `CharField` 的子类 ⇒ 具体的写在前面。
348
316
 
349
- 🔴 文件列**没有单独的类型**:`FileField` 在库里就是一列 varchar,所以按 `text` 报。
317
+ 🔴 这个函数在本文件里**只能有一份**:曾经在下面又写了一份把这份整个遮掉,
318
+ 于是 `FileField` 报成 `text`、`DateTimeField` 报成 `date` —— 而两份代码看着都"挺对"。
350
319
  """
351
320
  from django.db import models
352
321
 
322
+ # 关联列:这张表里那一列的类型 = **对端主键**那一列的类型(`order_id` 是 bigint,不是 text)。
323
+ # ⚠️ 多对多在这一层没有自己的列(列在中间表上),而 `shape()` 也不会把 m2m 送到这里来。
324
+ target = getattr(field, 'target_field', None)
325
+ if target is not None and target is not field and not getattr(field, 'many_to_many', False):
326
+ return _type_of(target)
327
+
353
328
  mapping = (
354
- (models.BigAutoField, 'bigint'),
329
+ (models.BigAutoField, 'bigint'), # 子类链:BigAutoField ⊂ BigIntegerField ⊂ IntegerField
355
330
  (models.BigIntegerField, 'bigint'),
331
+ (models.SmallIntegerField, 'integer'),
332
+ (models.PositiveIntegerField, 'integer'),
356
333
  (models.AutoField, 'integer'),
357
334
  (models.IntegerField, 'integer'),
358
- (models.SmallIntegerField, 'integer'),
359
335
  (models.BooleanField, 'boolean'),
336
+ (models.DateTimeField, 'timestamp'), # 🔴 必须排在 DateField 前面(否则 created_at 报成 date)
360
337
  (models.DateField, 'date'),
361
- (models.DateTimeField, 'timestamp'),
338
+ (models.TimeField, 'time'),
339
+ (models.DurationField, 'interval'),
362
340
  (models.DecimalField, 'numeric'),
363
341
  (models.FloatField, 'real'),
364
342
  (models.JSONField, 'jsonb'),
343
+ (models.UUIDField, 'uuid'),
344
+ (models.BinaryField, 'bytea'),
345
+ (models.GenericIPAddressField, 'inet'),
346
+ # 下面这一档在库里都是 varchar —— 包括"文件字段"(它只是 varchar + 上传逻辑)
347
+ (models.ImageField, 'varchar'), # 🔴 必须排在 FileField 前面
348
+ (models.FileField, 'varchar'),
349
+ (models.EmailField, 'varchar'),
350
+ (models.URLField, 'varchar'),
351
+ (models.SlugField, 'varchar'),
352
+ (models.CharField, 'varchar'),
353
+ (models.TextField, 'text'),
365
354
  )
366
355
  for cls, name in mapping:
367
356
  if isinstance(field, cls):
368
357
  return name
369
- if isinstance(field, (models.CharField, models.SlugField)):
370
- return 'varchar'
371
358
  return 'text'
372
359
 
373
360
 
@@ -168,6 +168,44 @@ body {
168
168
  z-index: 2;
169
169
  }
170
170
 
171
+ /* ── 左边那条车道:硬边界 + 跨模块总线(见 canvas.js 的 `boundaryLayer()`) ──────
172
+ * 🔴 边界是**硬**的:`applyView()` 夹住平移,过不去;所以它必须看得见,
173
+ * 否则"怎么拖都拖不动"会像页面卡住了。
174
+ * 🔴 边界**左边**那一块铺一层很淡的底:那是"没有内容、也过不去"的区域。 */
175
+ .boundary-line {
176
+ stroke: var(--line);
177
+ stroke-width: 1.5;
178
+ stroke-dasharray: 7 5;
179
+ }
180
+ .boundary-rail {
181
+ stroke: var(--line);
182
+ stroke-width: 1;
183
+ stroke-dasharray: 2 4;
184
+ opacity: .9;
185
+ }
186
+ .boundary-outside { fill: var(--ink); opacity: .04; }
187
+ .boundary-label {
188
+ fill: var(--muted);
189
+ font-size: 11px;
190
+ user-select: none;
191
+ }
192
+ .boundary-label.is-rail { opacity: .85; }
193
+ /* 跨模块那条线:走法是"横 → 沿总线竖 → 横",与其他线的斜直连一眼能分开 */
194
+ .edge.cross-module { stroke-linecap: square; }
195
+ /* 跨模块"出/进本模块"的那截短线(对端在别的模块里,卡片没画出来):
196
+ * 🔴 画成**点线**:与实线(真的连到了对端)区分开 —— 一眼看出"这条是走到边界为止的"。
197
+ * ⚠️ 与只读那条(`5 4` 虚线)也不同,免得两种含义糊在一起。 */
198
+ .edge.cross-module.is-stub { stroke-dasharray: 1 3; }
199
+ /* 跨模块的关联名放在总线右边那条空道上(框与直连那套同一个样式,只是位置固定) */
200
+ .edge-name.cross-module { text-anchor: middle; }
201
+ /* 短线(对端在别的模块)那几条的标签:**左对齐**放在线上方(见 canvas.js 里那段说明) */
202
+ .edge-name.cross-module.is-stub { text-anchor: start; }
203
+ /* 跨模块短线在总线那一头的"端帽":表示接上了总线,而不是线断了 */
204
+ .edge-cap { fill: none; stroke: var(--ink); stroke-width: 1.6; stroke-linecap: round; }
205
+ .edge-cap.is-added { stroke: var(--added); }
206
+ .edge-cap.is-modified { stroke: var(--modified); }
207
+ .edge-cap.is-deleted { stroke: var(--deleted); }
208
+
171
209
  /* 🔴 **默认是黑的**(用户明确要求):连线只有在"这条关联相对基线动过"时才上色 ——
172
210
  * 没动过的线不该抢眼,颜色留给"变了的东西"(与卡片、字段行那三档同一套 token)。 */
173
211
  .edge {
@@ -280,9 +318,11 @@ body {
280
318
 
281
319
  /* ── 一张表的卡片 ─────────────────────────────────────────────────────── */
282
320
 
321
+ /* 🔴 卡片宽度:去掉「库类型」那一列之后只需要 **340px**(字段名 150 + 种类 80 + 备注 110)——
322
+ * 三列宽都是写死的(见 `card()` 里那份 `<colgroup>`),所以宽度必须跟它对齐。 */
283
323
  .table {
284
324
  position: absolute;
285
- width: 320px;
325
+ width: 340px;
286
326
  background: var(--card);
287
327
  border: 1px solid var(--line);
288
328
  border-radius: 10px;
@@ -292,16 +332,30 @@ body {
292
332
  }
293
333
  .table:hover { box-shadow: 0 2px 4px rgb(15 23 42 / 8%), 0 12px 32px rgb(15 23 42 / 8%); }
294
334
 
295
- /* 三态:**整块染底色** + 表头加深 + 左侧一道色条。
296
- * 🔴 底色是主判据、色条是辅助:一眼扫过去先看到"哪几张表变了",再看左条确认是加还是删。 */
335
+ /* 三态:**只有"整张表是新的 / 没了"才整块染底色**(`added` / `deleted`)。
336
+ * 🔴 `modified`(表里**有**字段或关联变过)**不再染色**(用户明确要的:
337
+ * "只有那个字段或者关联变色,而不是整个表变色")—— 逐行 / 逐条线在下面单独上色,
338
+ * 卡片标题旁只挂一个灰的「改 N」,用来在几十张表里找得到是哪张。
339
+ * 🔴 新的表为什么仍然整卡染:基线里根本没有这张表,逐列差异无从谈起,
340
+ * 不染的话"新表"这件事就完全没提示了(它的每一行也会跟着标成 added)。 */
297
341
  .table.added { border-color: color-mix(in srgb, var(--added) 45%, var(--line)); background: var(--tint-added); }
298
- .table.modified { border-color: color-mix(in srgb, var(--modified) 45%, var(--line)); background: var(--tint-modified); }
299
342
  .table.deleted { border-color: color-mix(in srgb, var(--deleted) 40%, var(--line)); background: var(--tint-deleted); opacity: .78; }
300
343
  .table.added > .head { background: var(--tint-added-strong); box-shadow: inset 3px 0 0 var(--added); }
301
- .table.modified > .head { background: var(--tint-modified-strong); box-shadow: inset 3px 0 0 var(--modified); }
302
344
  .table.deleted > .head { background: var(--tint-deleted-strong); box-shadow: inset 3px 0 0 var(--deleted); }
303
- /* 没变过的表保持素白(否则"变了"这件事就不显眼了) */
304
- .table:not(.added):not(.modified):not(.deleted) > .head { background: var(--head); }
345
+ /* 没变过的表(含"改过"那种)保持素白:颜色留给真的变了的那几行 / 那几条线 */
346
+ .table:not(.added):not(.deleted) > .head { background: var(--head); }
347
+
348
+ /* 标题旁那个灰计数:表里有几处变过(**不是颜色**,只是个索引) */
349
+ .diff-count {
350
+ margin-left: 8px;
351
+ padding: 1px 6px;
352
+ border: 1px solid var(--line);
353
+ border-radius: 999px;
354
+ color: var(--muted);
355
+ font-size: 11px;
356
+ font-family: ui-monospace, Consolas, monospace;
357
+ vertical-align: 1px;
358
+ }
305
359
 
306
360
  .table.is-flash { animation: flash 1.2s ease; }
307
361
  @keyframes flash {
@@ -367,47 +421,43 @@ body {
367
421
  .note p { margin: 0 0 6px; font-size: 13px; }
368
422
 
369
423
  /* ── 字段行 ──────────────────────────────────────────────────────────────
370
- * 🔴 这里用 **CSS grid** 而不是 `<table>` 的自动布局:字段行要支持"列序翻转"
371
- * (字段名优先 ⇄ 备注优先),而 CSS 的 `order` 对 `<td>` **不生效**
372
- * (真踩过:按钮文案变了、表格一个格子都没动)。grid 里 `order` 是正经生效的。
373
- * `<table>` / `<tr>` / `<td>` 只是保留下来当语义与 DOM 结构,显示全部走 grid。
374
- * 🔴 每个轨道都要写**最小可用宽**(`minmax(min-content, …)`)而不是 `minmax(0, …)`:
375
- * 后者允许把列压到 0 宽,几列就叠在一起了(真踩过:备注优先时 `created_at` 与"时间"重合)。
376
- * 挤压时宁可横向溢出,也不能把两列画在同一个位置。
424
+ * 🔴 用**真表格布局 + 固定的列宽**(`table-layout: fixed` + `card()` 写的那份 `<colgroup>`):
425
+ * 列宽固定 ⇒ 每行的四列天然对齐(用户要的「每个字段的每个列都能够对齐」),
426
+ * 而且**长备注不会把别的列挤走**(用户要的「行宽可以固定,有需要用户会自己点开看」)——
427
+ * 备注那一格只显示一行、超出用 `…` 截断,想看全文点那一行(`tr.detail` 里有整段)。
428
+ * 原来"每个字段一行、各自一个 grid"的写法连对齐都做不到(列宽按每一行自己的内容算)。
429
+ * (也试过"整张表一个 grid + `display: contents`":轨道共用了,但自动排布会被跨列的
430
+ * detail 行带偏 —— 实测 `customer` 那行的备注格跑到了第一列。所以用表格布局。)
431
+ * 🔴 列序翻转(备注优先 ⇄ 字段名优先)**不靠 CSS 的 `order`**(`<td>` 上根本不生效,
432
+ * 那是 grid 才有的),改由 `fieldRow()` **按当前模式决定 append 顺序**,宽度也跟着换一套。
377
433
  */
378
- table.cols { display: grid; width: 100%; }
379
- table.cols tbody { display: grid; }
380
- table.cols tr.field {
381
- display: grid;
382
- grid-template-columns:
383
- minmax(min-content, auto) minmax(min-content, auto) minmax(min-content, auto) minmax(0, 1fr);
384
- align-items: stretch;
385
- }
386
- table.cols tr.detail { display: none; grid-template-columns: 1fr; }
387
- table.cols tr.field.is-open + tr.detail { display: grid; }
388
- table.cols tr.detail > td { grid-column: 1 / -1; }
434
+ table.cols { width: 100%; table-layout: fixed; border-collapse: collapse; }
435
+ table.cols tr.detail { display: none; }
436
+ table.cols tr.field.is-open + tr.detail { display: table-row; }
437
+ table.cols tr.detail > td { padding: 6px 10px 10px 22px; white-space: normal; }
438
+ /* 「关联字段(外键列)」那一组默认**合上**(要再打开,见 `card()` 里那个 toggle 行) */
439
+ table.cols tbody[hidden] { display: none; }
440
+ table.cols tr.fk-toggle > td {
441
+ cursor: pointer;
442
+ color: var(--muted);
443
+ font-size: 11.5px;
444
+ border-top: 1px dashed var(--line);
445
+ }
446
+ table.cols tr.fk-toggle .caret-mini { display: inline-block; font-style: normal; transition: transform .12s; }
447
+ table.cols tr.fk-toggle.is-open .caret-mini { transform: rotate(90deg); }
389
448
 
390
449
  table.cols td {
391
- padding: 4px 10px;
450
+ padding: 4px 8px;
392
451
  border-top: 1px solid var(--line-soft);
393
452
  font-size: 12.5px;
394
- overflow-wrap: anywhere;
453
+ /* 🔴 固定列宽 + 一行截断:三列都 nowrap,超了给 `…`(`title` 里挂着全文,鼠标停一下也能看) */
454
+ white-space: nowrap;
455
+ overflow: hidden;
456
+ text-overflow: ellipsis;
395
457
  }
396
458
  table.cols tr.field:first-child td { border-top: 0; }
397
459
 
398
- /* 「备注优先」:**备注 | 字段名 | 种类 | 库类型**。
399
- * 只翻 `order` 不够 —— 列宽也得换,否则备注列还是那条窄缝(等于没翻)。
400
- * 🔴 备注列给**固定宽**,不给 `fr`:它是这张网格里**唯一**的弹性轨道,
401
- * `0.5fr` 和 `9fr` 算出来一样宽(都吃掉全部剩余)—— 真踩过,改了半天没反应。
402
- * 固定 128px 之后,字段名那一列自然贴到左边,右侧多出来的留白(收窄卡片或拖宽都行)。 */
403
- #canvas.is-note-first table.cols tr.field {
404
- grid-template-columns:
405
- 116px minmax(min-content, auto) minmax(min-content, auto) minmax(min-content, auto);
406
- justify-content: start;
407
- }
408
- #canvas.is-note-first td.brief { order: -1; }
409
- /* 备注优先时把卡片放宽一点:四列里有一列是整句说明,320px 是字段名优先那套的宽度。 */
410
- #canvas.is-note-first .table { width: 380px; }
460
+ /* 备注优先时卡片**不用再放宽**:两种模式都是 400px(列宽固定,见 `.table` 那一段的说明)。 */
411
461
 
412
462
  /* 一格备注都没有时那个破折号:压暗,别让人以为"这里有内容" */
413
463
  td.brief.empty { color: var(--line); }
@@ -453,13 +503,9 @@ td.kind .mark {
453
503
  .mark.amber { color: var(--tone-amber); }
454
504
  .mark.teal { color: var(--tone-teal); }
455
505
 
456
- td.type {
457
- color: var(--muted);
458
- text-align: right;
459
- white-space: nowrap;
460
- font-family: ui-monospace, Consolas, monospace;
461
- font-size: 11.5px;
462
- }
506
+ /* 🔴 「库类型」那一列**去掉了**(用户要的:"这些都不需要了")——
507
+ * `bigint` / `text` / `varchar` 这些是"库里怎么存的",人看的是左边那个"种类"小标
508
+ * (整数 / 长文本 / 文件 / 时间…)。真要看库类型时,点开那一行,详情里有。 */
463
509
 
464
510
  td.brief { color: var(--muted); }
465
511
  td.brief.muted { opacity: .75; }
@@ -519,6 +565,13 @@ tr.detail .note-text { color: var(--muted); }
519
565
  cursor: pointer;
520
566
  }
521
567
  .rel:hover { background: #f1f5f9; }
568
+ /* 关联那一行也按"这条动过没有"上色(与画布上那条线同一个判据):**只染这一行** */
569
+ .rel.is-added { background: var(--tint-added-strong); }
570
+ .rel.is-modified { background: var(--tint-modified-strong); }
571
+ .rel.is-deleted { background: var(--tint-deleted-strong); text-decoration: line-through; text-decoration-color: color-mix(in srgb, var(--deleted) 55%, transparent); }
572
+ .rel.is-added .rel-name { color: var(--added); }
573
+ .rel.is-modified .rel-name { color: var(--modified); }
574
+ .rel.is-deleted .rel-name { color: var(--deleted); }
522
575
  .rel .rel-name { font-family: ui-monospace, Consolas, monospace; }
523
576
  .rel .rel-to { margin-left: auto; color: var(--accent); font-size: 12px; }
524
577