@thatopen/services 0.7.0 → 0.9.0

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 (77) hide show
  1. package/README.md +62 -62
  2. package/dist/built-in/index.d.ts +66 -41
  3. package/dist/cli/lib/revit-addin.d.ts +8 -0
  4. package/dist/cli.js +154 -13
  5. package/dist/core/client.d.ts +6 -0
  6. package/dist/core/platform-client.d.ts +139 -0
  7. package/dist/core/platform-client.live.test.d.ts +1 -0
  8. package/dist/core/platform-client.notifications.test.d.ts +1 -0
  9. package/dist/index.cjs.js +2 -2
  10. package/dist/index.d.ts +1 -0
  11. package/dist/index.es.js +635 -445
  12. package/dist/types/notifications.d.ts +54 -0
  13. package/dist/vite-env.d.ts +1 -1
  14. package/docs/ai-quickstart.md +120 -1
  15. package/docs/cli/create.md +29 -29
  16. package/docs/cli/local-server.md +38 -38
  17. package/docs/cli/login.md +32 -32
  18. package/docs/cli/publish.md +31 -31
  19. package/docs/cli/run.md +26 -26
  20. package/docs/cli/serve.md +43 -43
  21. package/docs/cli/swap.md +30 -30
  22. package/docs/flow-plugin-guide.md +26 -0
  23. package/docs/revit-collab-quickstart.md +26 -17
  24. package/docs/rhino-interop-quickstart.md +74 -7
  25. package/package.json +1 -1
  26. package/resources/AGENTS.md +40 -40
  27. package/src/built-in/CDEManager/example.ts +214 -214
  28. package/src/built-in/CDEManager/examples/FileEditing/example.ts +226 -226
  29. package/src/built-in/CDEManager/examples/FileMoves/example.ts +186 -186
  30. package/src/built-in/CDEManager/examples/FileOperations/example.ts +295 -295
  31. package/src/built-in/CDEManager/examples/FileTransfers/example.ts +210 -210
  32. package/src/built-in/CDEManager/examples/FolderManagement/example.ts +230 -230
  33. package/src/built-in/CDEManager/examples/MetadataSchema/example.ts +230 -230
  34. package/src/built-in/CDEManager/examples/MetadataViews/example.ts +166 -166
  35. package/src/built-in/CDEManager/examples/ProjectConfig/example.ts +177 -177
  36. package/src/built-in/UIManager/src/app/example.ts +97 -97
  37. package/src/built-in/UIManager/src/app/examples/AppState/example.ts +140 -140
  38. package/src/built-in/UIManager/src/app/examples/Banner/example.ts +159 -159
  39. package/src/built-in/UIManager/src/app/examples/Contexts/example.ts +123 -123
  40. package/src/built-in/UIManager/src/app/examples/FileDrop/example.ts +124 -124
  41. package/src/built-in/UIManager/src/app/examples/FloatingLayout/example.ts +90 -90
  42. package/src/built-in/UIManager/src/app/examples/Modal/example.ts +163 -163
  43. package/src/built-in/UIManager/src/app/examples/Notifications/example.ts +133 -133
  44. package/src/built-in/UIManager/src/app/examples/ProjectFileStorage/example.ts +150 -150
  45. package/src/built-in/UIManager/src/app/examples/Sidebar/example.ts +102 -102
  46. package/src/built-in/UIManager/src/clashes-panel/example.ts +183 -183
  47. package/src/built-in/UIManager/src/git-history-panel/example.ts +70 -70
  48. package/src/built-in/UIManager/src/models-list/example.ts +122 -122
  49. package/src/built-in/UIManager/src/topics-panel/example.ts +170 -170
  50. package/src/built-in/index.ts +66 -41
  51. package/src/cli/templates/app/index.html +35 -35
  52. package/src/cli/templates/app/package.json +32 -32
  53. package/src/cli/templates/app/src/bim-components/CloudRunner/index.ts +95 -95
  54. package/src/cli/templates/app/src/bim-components/CloudRunner/src/index.ts +1 -1
  55. package/src/cli/templates/app/src/bim-components/CloudRunner/src/types.ts +5 -5
  56. package/src/cli/templates/app/src/bim-components/index.ts +1 -1
  57. package/src/cli/templates/app/src/globals.ts +1 -1
  58. package/src/cli/templates/app/src/main.history.ts +191 -0
  59. package/src/cli/templates/app/src/ui-components/app-info-section/index.ts +26 -26
  60. package/src/cli/templates/app/src/ui-components/app-info-section/src/index.ts +1 -1
  61. package/src/cli/templates/app/src/ui-components/app-info-section/src/types.ts +15 -15
  62. package/src/cli/templates/app/src/ui-components/cloud-runner-section/index.ts +37 -37
  63. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/index.ts +1 -1
  64. package/src/cli/templates/app/src/ui-components/cloud-runner-section/src/types.ts +14 -14
  65. package/src/cli/templates/app/src/ui-components/index.ts +2 -2
  66. package/src/cli/templates/app/tsconfig.json +16 -16
  67. package/src/cli/templates/app/vite.config.js +57 -57
  68. package/src/cli/templates/cloud-component/README.md +205 -205
  69. package/src/cli/templates/cloud-component/_thatopen +5 -5
  70. package/src/cli/templates/cloud-component/declarations.json +4 -4
  71. package/src/cli/templates/cloud-component/package.json +21 -21
  72. package/src/cli/templates/cloud-component/src/main.ts +70 -70
  73. package/src/cli/templates/cloud-component/tsconfig.json +16 -16
  74. package/src/cli/templates/cloud-component/vite.config.js +46 -46
  75. package/src/cli/templates/shared/AGENTS.md +8 -8
  76. package/src/cli/templates/shared/CLAUDE.md +1 -1
  77. package/src/cli/templates/shared/_gitignore +5 -5
@@ -1,70 +1,70 @@
1
- /* MD
2
- ## git-history-panel
3
- ---
4
- `top-git-history` is a GitKraken-style commit timeline for the **revit-flow**
5
- feature. It lists the commits pushed by the Revit add-in (newest first) and,
6
- when you click a commit, drives the shared 3D viewer to color that commit's
7
- changed elements — created green, modified blue, deleted red — on the loaded
8
- baseline model.
9
-
10
- All viewer mutation lives in the headless `GitHistoryManager`; the panel is
11
- intent-only. Mounting it is a single line of markup once `UIManager` and
12
- `GitHistoryManager` are in the setup call.
13
- */
14
-
15
- import { html } from "lit";
16
- import * as THREE from "three";
17
- import * as OBC from "@thatopen/components";
18
- import * as OBF from "@thatopen/components-front";
19
- import * as FRAGS from "@thatopen/fragments";
20
- import * as BUI from "@thatopen/ui";
21
- import { PlatformClient, UIManager, GitHistoryManager } from "@thatopen/services";
22
- import type { App } from "../app/index";
23
-
24
-
25
- const client = PlatformClient.fromPlatformContext();
26
-
27
- const { components } = (await client.setup(
28
- { OBC, OBF, BUI, THREE, FRAGS },
29
- { uuid: UIManager.uuid },
30
- { uuid: GitHistoryManager.uuid },
31
- )) as { components: OBC.Components };
32
-
33
- components.get(UIManager).init();
34
-
35
- // The panel self-wires init(client) on connect, but calling it here keeps the
36
- // timeline populated before the panel mounts.
37
- await components.get(GitHistoryManager).init(client);
38
-
39
- const app = document.createElement("top-app") as unknown as App;
40
-
41
- app.setup = (waitUntil) => {
42
- waitUntil(
43
- (async () => {
44
- const fragments = components.get(OBC.FragmentsManager);
45
- const workerUrl = await FRAGS.FragmentsModels.getWorker();
46
- fragments.init(await FRAGS.toClassicWorker(workerUrl), { classicWorker: true });
47
- })(),
48
- "Fragments Core",
49
- );
50
- return { components, client };
51
- };
52
-
53
- app.elements = {
54
- viewer: () => html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
55
- history: () => html`<top-git-history></top-git-history>`,
56
- };
57
-
58
- app.layouts = {
59
- main: {
60
- label: "Main",
61
- icon: "solar:branching-paths-up-bold",
62
- template: `"viewer history" 1fr / 1fr 24rem`,
63
- },
64
- };
65
-
66
- app.layout = "main";
67
-
68
- const container = document.getElementById("that-open-app") ?? document.body;
69
- container.appendChild(app);
70
- document.body.style.margin = "0";
1
+ /* MD
2
+ ## git-history-panel
3
+ ---
4
+ `top-git-history` is a GitKraken-style commit timeline for the **revit-flow**
5
+ feature. It lists the commits pushed by the Revit add-in (newest first) and,
6
+ when you click a commit, drives the shared 3D viewer to color that commit's
7
+ changed elements — created green, modified blue, deleted red — on the loaded
8
+ baseline model.
9
+
10
+ All viewer mutation lives in the headless `GitHistoryManager`; the panel is
11
+ intent-only. Mounting it is a single line of markup once `UIManager` and
12
+ `GitHistoryManager` are in the setup call.
13
+ */
14
+
15
+ import { html } from "lit";
16
+ import * as THREE from "three";
17
+ import * as OBC from "@thatopen/components";
18
+ import * as OBF from "@thatopen/components-front";
19
+ import * as FRAGS from "@thatopen/fragments";
20
+ import * as BUI from "@thatopen/ui";
21
+ import { PlatformClient, UIManager, GitHistoryManager } from "@thatopen/services";
22
+ import type { App } from "../app/index";
23
+
24
+
25
+ const client = PlatformClient.fromPlatformContext();
26
+
27
+ const { components } = (await client.setup(
28
+ { OBC, OBF, BUI, THREE, FRAGS },
29
+ { uuid: UIManager.uuid },
30
+ { uuid: GitHistoryManager.uuid },
31
+ )) as { components: OBC.Components };
32
+
33
+ components.get(UIManager).init();
34
+
35
+ // The panel self-wires init(client) on connect, but calling it here keeps the
36
+ // timeline populated before the panel mounts.
37
+ await components.get(GitHistoryManager).init(client);
38
+
39
+ const app = document.createElement("top-app") as unknown as App;
40
+
41
+ app.setup = (waitUntil) => {
42
+ waitUntil(
43
+ (async () => {
44
+ const fragments = components.get(OBC.FragmentsManager);
45
+ const workerUrl = await FRAGS.FragmentsModels.getWorker();
46
+ fragments.init(await FRAGS.toClassicWorker(workerUrl), { classicWorker: true });
47
+ })(),
48
+ "Fragments Core",
49
+ );
50
+ return { components, client };
51
+ };
52
+
53
+ app.elements = {
54
+ viewer: () => html`<top-viewer><top-viewer-tools></top-viewer-tools></top-viewer>`,
55
+ history: () => html`<top-git-history></top-git-history>`,
56
+ };
57
+
58
+ app.layouts = {
59
+ main: {
60
+ label: "Main",
61
+ icon: "solar:branching-paths-up-bold",
62
+ template: `"viewer history" 1fr / 1fr 24rem`,
63
+ },
64
+ };
65
+
66
+ app.layout = "main";
67
+
68
+ const container = document.getElementById("that-open-app") ?? document.body;
69
+ container.appendChild(app);
70
+ document.body.style.margin = "0";
@@ -1,122 +1,122 @@
1
- /* MD
2
- ## top-models-list
3
- ---
4
- top-models-list is the project's 3D-viewable-files tree: it mirrors the real storage folder structure, converts IFC to fragments, and loads `.frag`/`.potree`/`.splat` models into the 3D scene. Its loaders are PLUGGABLE — the panel owns the UI and the orchestration (progress, refresh, association), but the HOST decides how each extra file format is loaded, so you can teach it new formats without forking the built-in.
5
-
6
- This tutorial covers the two extension points. First, registering a custom loader for a file extension — either imperatively with `registerLoader(ext, fn)` or up front via the `loaders` property; a loader receives `(fileId, ctx)` where `ctx` carries the engine (`components`), the platform `client`, and per-file alignment persistence. Second, overriding the IFC to fragments `converter` — omit it to use the built-in default (which drives the project's IfcFragmenter cloud component, resolved by its stable name — see `adrs/resolve-ifc-fragmenter-by-stable-name.md`), or provide one to run conversion elsewhere.
7
-
8
- A behaviour worth knowing: registering a loader does NOT control whether a file shows up in the tree — the tree always mirrors the project's real storage folders, so an unrecognized file is still listed as long as it's a source (IFC/LAS/PLY) or derivative (`.frag`/`.potree`/`.splat`) extension; the loaders registry only decides whether a file's row gets a load action or none.
9
-
10
- Known gaps (intentionally deferred, not forgotten): file upload and the base-model/auto-coordinate settings aren't ported, so `allow-upload` is currently a no-op. Everything else per-file is here, including editable attach/detach via a picker — MINUS delete, dropped on purpose. See `models-list/legacy-example.ts` + `models-list/impl.ts` for the retired implementation those gaps would be ported from.
11
-
12
- By the end you'll have a files tree that knows how to load a custom format and how to run a custom IFC conversion, with the panel's UI untouched.
13
- */
14
-
15
- import { html } from "lit";
16
- import * as THREE from "three";
17
- import * as OBC from "@thatopen/components";
18
- import * as OBF from "@thatopen/components-front";
19
- import * as FRAGS from "@thatopen/fragments";
20
- import * as BUI from "@thatopen/ui";
21
- import { PlatformClient, UIManager } from "@thatopen/services";
22
- import type { App } from "../app/index";
23
- import type { ModelsListV2 } from "./src/v2/models-list-panel";
24
-
25
-
26
- const client = PlatformClient.fromPlatformContext();
27
-
28
- // UIManager registers every platform web component (top-app, top-viewer,
29
- // top-models-list, …) before the DOM renders.
30
- const { components } = (await client.setup(
31
- { OBC, OBF, BUI, THREE, FRAGS },
32
- { uuid: UIManager.uuid },
33
- )) as { components: OBC.Components };
34
-
35
- components.get(UIManager).init();
36
-
37
- const app = document.createElement("top-app") as unknown as App;
38
-
39
- app.setup = (waitUntil) => {
40
- waitUntil(
41
- (async () => {
42
- const fragments = components.get(OBC.FragmentsManager);
43
- const workerUrl = await FRAGS.FragmentsModels.getWorker();
44
- fragments.init(await FRAGS.toClassicWorker(workerUrl), {
45
- classicWorker: true,
46
- });
47
- })(),
48
- "Fragments Core",
49
- );
50
- return { components, client };
51
- };
52
-
53
- // One stable <top-models-list>, kept by reference so re-rendering top-app
54
- // reuses it. The loaders are read LIVE, so registration can happen at any time.
55
- const modelsList = document.createElement("top-models-list") as ModelsListV2;
56
-
57
- // ── Extension point 1: a custom loader, keyed by file extension ──────────────
58
- // A ModelLoader is `(fileId, ctx) => void | Promise<void>`. The panel calls it
59
- // when the user loads a file whose extension matches. `ctx` gives you everything
60
- // you need: { name, ext, components, client, getAlignment, setAlignment }. Do
61
- // whatever it takes to put the file in the scene — fetch it via the client,
62
- // parse it, and add geometry to a world from `components`.
63
- modelsList.registerLoader("xyz", async (fileId, ctx) => {
64
- // const buffer = await ctx.client
65
- // .downloadFile(fileId)
66
- // .then((r: Response) => r.arrayBuffer());
67
- // ...parse `buffer` and add it to the world here...
68
- // Persist/restore a placement matrix across reloads via the panel's app-data:
69
- // const saved = ctx.getAlignment(fileId);
70
- // ctx.setAlignment(fileId, matrix.toArray());
71
- console.log(`[xyz loader] would load "${ctx.name}" (${fileId})`, ctx.components);
72
- });
73
-
74
- // Equivalent declarative form — hand the whole registry over at once:
75
- // modelsList.loaders = { xyz: async (fileId, ctx) => { ... } };
76
- //
77
- // Registering a loader for an extension gives its files a load action here —
78
- // but doesn't control visibility (see the note above). For a CONVERT-style
79
- // action — e.g. a PLY that runs a PLY→3D-tiles cloud component then views the
80
- // result — register a RICH entry so the button reads correctly:
81
- // modelsList.registerLoader("ply", {
82
- // label: "Generate 3D tiles",
83
- // icon: "mdi:cube-scan",
84
- // fn: async (fileId, ctx) => {
85
- // // const { executionId } = await ctx.client.executeComponent(...);
86
- // // ...poll until done, then open the produced tileset...
87
- // },
88
- // });
89
-
90
- // ── Extension point 2: override the IFC → fragments converter ────────────────
91
- // OMIT this to keep the built-in default (drives the project's IfcFragmenter
92
- // cloud component via the platform client). Provide it to run conversion
93
- // elsewhere / differently — the panel keeps the progress bar, reload-survival,
94
- // association and refresh; your converter just `start`s and is `poll`ed.
95
- modelsList.converter = {
96
- async start(fileId) {
97
- // kick off your conversion; return an execution id to poll
98
- return `exec-${fileId}`;
99
- },
100
- async poll(executionId) {
101
- // report progress; when done, hand back the produced .frag id
102
- return { done: true, ok: true, fragId: "demo-frag", message: "Converted" };
103
- },
104
- };
105
-
106
- // Mount the panel into a one-area layout. (In a real app it sits beside a
107
- // top-viewer so loaded models have a world to render into.)
108
- app.elements = {
109
- files: () => html`${modelsList}`,
110
- };
111
- app.layouts = {
112
- main: {
113
- label: "Files",
114
- icon: "mdi:folder-multiple-outline",
115
- template: `"files" 1fr / 1fr`,
116
- },
117
- };
118
- app.layout = "main";
119
-
120
- const container = document.getElementById("that-open-app") ?? document.body;
121
- container.appendChild(app);
122
- document.body.style.margin = "0";
1
+ /* MD
2
+ ## top-models-list
3
+ ---
4
+ top-models-list is the project's 3D-viewable-files tree: it mirrors the real storage folder structure, converts IFC to fragments, and loads `.frag`/`.potree`/`.splat` models into the 3D scene. Its loaders are PLUGGABLE — the panel owns the UI and the orchestration (progress, refresh, association), but the HOST decides how each extra file format is loaded, so you can teach it new formats without forking the built-in.
5
+
6
+ This tutorial covers the two extension points. First, registering a custom loader for a file extension — either imperatively with `registerLoader(ext, fn)` or up front via the `loaders` property; a loader receives `(fileId, ctx)` where `ctx` carries the engine (`components`), the platform `client`, and per-file alignment persistence. Second, overriding the IFC to fragments `converter` — omit it to use the built-in default (which drives the project's IfcFragmenter cloud component, resolved by its stable name — see `adrs/resolve-ifc-fragmenter-by-stable-name.md`), or provide one to run conversion elsewhere.
7
+
8
+ A behaviour worth knowing: registering a loader does NOT control whether a file shows up in the tree — the tree always mirrors the project's real storage folders, so an unrecognized file is still listed as long as it's a source (IFC/LAS/PLY) or derivative (`.frag`/`.potree`/`.splat`) extension; the loaders registry only decides whether a file's row gets a load action or none.
9
+
10
+ Known gaps (intentionally deferred, not forgotten): file upload and the base-model/auto-coordinate settings aren't ported, so `allow-upload` is currently a no-op. Everything else per-file is here, including editable attach/detach via a picker — MINUS delete, dropped on purpose. See `models-list/legacy-example.ts` + `models-list/impl.ts` for the retired implementation those gaps would be ported from.
11
+
12
+ By the end you'll have a files tree that knows how to load a custom format and how to run a custom IFC conversion, with the panel's UI untouched.
13
+ */
14
+
15
+ import { html } from "lit";
16
+ import * as THREE from "three";
17
+ import * as OBC from "@thatopen/components";
18
+ import * as OBF from "@thatopen/components-front";
19
+ import * as FRAGS from "@thatopen/fragments";
20
+ import * as BUI from "@thatopen/ui";
21
+ import { PlatformClient, UIManager } from "@thatopen/services";
22
+ import type { App } from "../app/index";
23
+ import type { ModelsListV2 } from "./src/v2/models-list-panel";
24
+
25
+
26
+ const client = PlatformClient.fromPlatformContext();
27
+
28
+ // UIManager registers every platform web component (top-app, top-viewer,
29
+ // top-models-list, …) before the DOM renders.
30
+ const { components } = (await client.setup(
31
+ { OBC, OBF, BUI, THREE, FRAGS },
32
+ { uuid: UIManager.uuid },
33
+ )) as { components: OBC.Components };
34
+
35
+ components.get(UIManager).init();
36
+
37
+ const app = document.createElement("top-app") as unknown as App;
38
+
39
+ app.setup = (waitUntil) => {
40
+ waitUntil(
41
+ (async () => {
42
+ const fragments = components.get(OBC.FragmentsManager);
43
+ const workerUrl = await FRAGS.FragmentsModels.getWorker();
44
+ fragments.init(await FRAGS.toClassicWorker(workerUrl), {
45
+ classicWorker: true,
46
+ });
47
+ })(),
48
+ "Fragments Core",
49
+ );
50
+ return { components, client };
51
+ };
52
+
53
+ // One stable <top-models-list>, kept by reference so re-rendering top-app
54
+ // reuses it. The loaders are read LIVE, so registration can happen at any time.
55
+ const modelsList = document.createElement("top-models-list") as ModelsListV2;
56
+
57
+ // ── Extension point 1: a custom loader, keyed by file extension ──────────────
58
+ // A ModelLoader is `(fileId, ctx) => void | Promise<void>`. The panel calls it
59
+ // when the user loads a file whose extension matches. `ctx` gives you everything
60
+ // you need: { name, ext, components, client, getAlignment, setAlignment }. Do
61
+ // whatever it takes to put the file in the scene — fetch it via the client,
62
+ // parse it, and add geometry to a world from `components`.
63
+ modelsList.registerLoader("xyz", async (fileId, ctx) => {
64
+ // const buffer = await ctx.client
65
+ // .downloadFile(fileId)
66
+ // .then((r: Response) => r.arrayBuffer());
67
+ // ...parse `buffer` and add it to the world here...
68
+ // Persist/restore a placement matrix across reloads via the panel's app-data:
69
+ // const saved = ctx.getAlignment(fileId);
70
+ // ctx.setAlignment(fileId, matrix.toArray());
71
+ console.log(`[xyz loader] would load "${ctx.name}" (${fileId})`, ctx.components);
72
+ });
73
+
74
+ // Equivalent declarative form — hand the whole registry over at once:
75
+ // modelsList.loaders = { xyz: async (fileId, ctx) => { ... } };
76
+ //
77
+ // Registering a loader for an extension gives its files a load action here —
78
+ // but doesn't control visibility (see the note above). For a CONVERT-style
79
+ // action — e.g. a PLY that runs a PLY→3D-tiles cloud component then views the
80
+ // result — register a RICH entry so the button reads correctly:
81
+ // modelsList.registerLoader("ply", {
82
+ // label: "Generate 3D tiles",
83
+ // icon: "mdi:cube-scan",
84
+ // fn: async (fileId, ctx) => {
85
+ // // const { executionId } = await ctx.client.executeComponent(...);
86
+ // // ...poll until done, then open the produced tileset...
87
+ // },
88
+ // });
89
+
90
+ // ── Extension point 2: override the IFC → fragments converter ────────────────
91
+ // OMIT this to keep the built-in default (drives the project's IfcFragmenter
92
+ // cloud component via the platform client). Provide it to run conversion
93
+ // elsewhere / differently — the panel keeps the progress bar, reload-survival,
94
+ // association and refresh; your converter just `start`s and is `poll`ed.
95
+ modelsList.converter = {
96
+ async start(fileId) {
97
+ // kick off your conversion; return an execution id to poll
98
+ return `exec-${fileId}`;
99
+ },
100
+ async poll(executionId) {
101
+ // report progress; when done, hand back the produced .frag id
102
+ return { done: true, ok: true, fragId: "demo-frag", message: "Converted" };
103
+ },
104
+ };
105
+
106
+ // Mount the panel into a one-area layout. (In a real app it sits beside a
107
+ // top-viewer so loaded models have a world to render into.)
108
+ app.elements = {
109
+ files: () => html`${modelsList}`,
110
+ };
111
+ app.layouts = {
112
+ main: {
113
+ label: "Files",
114
+ icon: "mdi:folder-multiple-outline",
115
+ template: `"files" 1fr / 1fr`,
116
+ },
117
+ };
118
+ app.layout = "main";
119
+
120
+ const container = document.getElementById("that-open-app") ?? document.body;
121
+ container.appendChild(app);
122
+ document.body.style.margin = "0";