Add random.ts helpers, replace all raw crypto.getRandomValues calls
New module packages/core/src/random.ts with randomHex, randomInt, randomIndex — centralizes all crypto-based randomness. Replaces: - Date.now() IDs in VariablesDialog (not unique, not random) - Hand-rolled hex token in automation/server.ts - Inline crypto.getRandomValues in clipboard.ts, use-collab.ts Also adds --color-component Tailwind theme color (#9747ff) and replaces all 6 hardcoded occurrences across DesignPanel, LayerTree, NodeContextMenuContent, and menu.ts.
This commit is contained in:
parent
921d2d35c5
commit
8d7dd0a36b
|
|
@ -1,5 +1,7 @@
|
|||
import { inflateSync, deflateSync } from 'fflate'
|
||||
|
||||
import { randomInt } from './random'
|
||||
|
||||
import {
|
||||
sceneNodeToKiwi,
|
||||
buildFigKiwi,
|
||||
|
|
@ -313,7 +315,7 @@ export function buildFigmaClipboardHTML(nodes: SceneNode[], graph: SceneGraph):
|
|||
type: 'NODE_CHANGES',
|
||||
sessionID: 0,
|
||||
ackID: 0,
|
||||
pasteID: crypto.getRandomValues(new Int32Array(1))[0],
|
||||
pasteID: randomInt(),
|
||||
pasteFileKey: 'openpencil',
|
||||
nodeChanges
|
||||
}
|
||||
|
|
|
|||
|
|
@ -281,3 +281,4 @@ export const CJK_FALLBACK_FAMILIES_LINUX = [
|
|||
]
|
||||
|
||||
export const CJK_GOOGLE_FONT = 'Noto Sans SC'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export type { GUID, Color, Vector, Matrix, Rect } from './types'
|
||||
export { degToRad, radToDeg, rotatePoint, rotatedCorners, rotatedBBox } from './geometry'
|
||||
export { randomHex, randomInt, randomIndex } from './random'
|
||||
|
||||
export * from './constants'
|
||||
|
||||
|
|
|
|||
14
packages/core/src/random.ts
Normal file
14
packages/core/src/random.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
export function randomHex(bytes = 16): string {
|
||||
const buf = crypto.getRandomValues(new Uint8Array(bytes))
|
||||
let hex = ''
|
||||
for (const b of buf) hex += b.toString(16).padStart(2, '0')
|
||||
return hex
|
||||
}
|
||||
|
||||
export function randomInt(): number {
|
||||
return crypto.getRandomValues(new Int32Array(1))[0]
|
||||
}
|
||||
|
||||
export function randomIndex(length: number): number {
|
||||
return crypto.getRandomValues(new Uint8Array(1))[0] % length
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
--color-surface: #e0e0e0;
|
||||
--color-muted: #888888;
|
||||
--color-input: #1e1e1e;
|
||||
--color-component: #9747ff;
|
||||
}
|
||||
|
||||
html,
|
||||
|
|
|
|||
|
|
@ -12,22 +12,15 @@ import {
|
|||
renderTreeNode,
|
||||
computeAllLayouts,
|
||||
selectionToJSX,
|
||||
sceneNodeToJSX
|
||||
sceneNodeToJSX,
|
||||
randomHex
|
||||
} from '@open-pencil/core'
|
||||
|
||||
import type { EditorStore } from '@/stores/editor'
|
||||
import type { ExportFormat } from '@open-pencil/core'
|
||||
|
||||
const TOKEN_LENGTH = 32
|
||||
|
||||
function generateToken(): string {
|
||||
const bytes = new Uint8Array(TOKEN_LENGTH)
|
||||
crypto.getRandomValues(bytes)
|
||||
return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')
|
||||
}
|
||||
|
||||
export function connectAutomation(getStore: () => EditorStore) {
|
||||
const token = generateToken()
|
||||
const token = randomHex(32)
|
||||
let ws: WebSocket | null = null
|
||||
let reconnectTimer: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ const isComponentType = computed(() => {
|
|||
data-test-id="design-node-header"
|
||||
class="flex items-center gap-1.5 border-b border-border px-3 py-2"
|
||||
>
|
||||
<span class="text-[11px]" :class="isComponentType ? 'text-[#9747ff]' : 'text-muted'">{{
|
||||
<span class="text-[11px]" :class="isComponentType ? 'text-component' : 'text-muted'">{{
|
||||
node.type
|
||||
}}</span>
|
||||
<span class="text-xs font-semibold">{{ node.name }}</span>
|
||||
|
|
@ -70,7 +70,7 @@ const isComponentType = computed(() => {
|
|||
>
|
||||
<button
|
||||
data-test-id="design-go-to-component"
|
||||
class="rounded bg-[#9747ff]/10 px-2 py-1 text-left text-[11px] text-[#9747ff] hover:bg-[#9747ff]/20"
|
||||
class="rounded bg-component/10 px-2 py-1 text-left text-[11px] text-component hover:bg-component/20"
|
||||
@click="store.goToMainComponent()"
|
||||
>
|
||||
Go to Main Component
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ function updateDropTarget(ev: PointerEvent) {
|
|||
class="size-3 shrink-0"
|
||||
:class="
|
||||
COMPONENT_TYPES.has(item.value.type)
|
||||
? 'text-[#9747ff] opacity-100'
|
||||
? 'text-component opacity-100'
|
||||
: 'opacity-70'
|
||||
"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ const separatorClass = menuSeparator({ class: 'my-1' })
|
|||
@select="store.createComponentFromSelection()"
|
||||
>
|
||||
<span>Create component</span>
|
||||
<span class="text-[11px] text-[#9747ff]/60">⌥⌘K</span>
|
||||
<span class="text-[11px] text-component/60">⌥⌘K</span>
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
v-if="canCreateComponentSet"
|
||||
|
|
@ -243,7 +243,7 @@ const separatorClass = menuSeparator({ class: 'my-1' })
|
|||
@select="store.createComponentSetFromComponents()"
|
||||
>
|
||||
<span>Create component set</span>
|
||||
<span class="text-[11px] text-[#9747ff]/60">⇧⌘K</span>
|
||||
<span class="text-[11px] text-component/60">⇧⌘K</span>
|
||||
</ContextMenuItem>
|
||||
<ContextMenuItem
|
||||
v-if="isComponent"
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import IconType from '~icons/lucide/type'
|
|||
import IconToggleLeft from '~icons/lucide/toggle-left'
|
||||
import IconX from '~icons/lucide/x'
|
||||
import ColorInput from './ColorInput.vue'
|
||||
import { colorToHexRaw, parseColor } from '@open-pencil/core'
|
||||
import { colorToHexRaw, parseColor, randomHex } from '@open-pencil/core'
|
||||
import { useEditorStore } from '@/stores/editor'
|
||||
import type { Variable, VariableCollection, VariableValue, Color } from '@open-pencil/core'
|
||||
|
||||
|
|
@ -186,7 +186,7 @@ function addVariable() {
|
|||
const col = store.graph.variableCollections.get(activeTab.value)
|
||||
if (!col) return
|
||||
|
||||
const id = `var:${Date.now()}`
|
||||
const id = `var:${randomHex(8)}`
|
||||
const valuesByMode: Record<string, VariableValue> = {}
|
||||
for (const mode of col.modes) {
|
||||
valuesByMode[mode.modeId] = { r: 0, g: 0, b: 0, a: 1 }
|
||||
|
|
@ -217,7 +217,7 @@ function addVariable() {
|
|||
}
|
||||
|
||||
function addCollection() {
|
||||
const id = `col:${Date.now()}`
|
||||
const id = `col:${randomHex(8)}`
|
||||
const collection: VariableCollection = {
|
||||
id,
|
||||
name: 'New collection',
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ const item = tv({
|
|||
tone: {
|
||||
default: 'text-surface',
|
||||
component:
|
||||
'text-[#9747ff] data-[disabled]:text-[#9747ff]/40 data-[highlighted]:bg-[#9747ff]/12'
|
||||
'text-component data-[disabled]:text-component/40 data-[highlighted]:bg-component/12'
|
||||
},
|
||||
justify: {
|
||||
between: 'justify-between gap-6',
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import {
|
|||
} from '@/constants'
|
||||
|
||||
import type { EditorStore } from '@/stores/editor'
|
||||
import { randomIndex } from '@open-pencil/core'
|
||||
import type { Color, SceneNode } from '@open-pencil/core'
|
||||
import type { Room } from 'trystero'
|
||||
|
||||
|
|
@ -40,7 +41,7 @@ export function useCollab(store: EditorStore) {
|
|||
roomId: null,
|
||||
peers: [],
|
||||
localName: storedName.value,
|
||||
localColor: PEER_COLORS[crypto.getRandomValues(new Uint8Array(1))[0] % PEER_COLORS.length]
|
||||
localColor: PEER_COLORS[randomIndex(PEER_COLORS.length)]
|
||||
})
|
||||
|
||||
let ydoc: Y.Doc | null = null
|
||||
|
|
@ -458,10 +459,9 @@ export function useCollab(store: EditorStore) {
|
|||
}
|
||||
|
||||
function generateRoomId(): string {
|
||||
const bytes = crypto.getRandomValues(new Uint8Array(ROOM_ID_LENGTH))
|
||||
let result = ''
|
||||
for (let i = 0; i < ROOM_ID_LENGTH; i++) {
|
||||
result += ROOM_ID_CHARS[bytes[i] % ROOM_ID_CHARS.length]
|
||||
result += ROOM_ID_CHARS[randomIndex(ROOM_ID_CHARS.length)]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue